繁体   English   中英

使用State Monad将我的所有函数转换为monadic函数

[英]Using State Monad turns all of my functions into monadic functions

我在Haskell中编写了一个加密库来学习加密和monad。 适合现实世界使用!)我的素数测试功能的类型是

prime :: (Integral a, Random a, RandomGen g) => a -> State g Bool

所以你可以看到我使用状态Monad所以我没有始终通过生成器的线程。 在内部,素数函数使用Miller-Rabin检验,该检验依赖于随机数,这就是素数函数也必须依赖随机数的原因。 它在某种程度上是有道理的,因为素数函数只进行概率测试。

仅供参考,整个主要功能如下,但我认为您不需要阅读它。

-- | findDS n, for odd n, gives odd d and s >= 0 s.t. n=2^s*d.
findDS :: Integral a => a -> (a, a)
findDS n = findDS' (n-1) 0
  where
    findDS' q s
      | even q = findDS' (q `div` 2) (s+1)
      | odd  q = (q,s)

-- | millerRabinOnce n d s a does one MR round test on
-- n using a.
millerRabinOnce :: Integral a => a -> a -> a -> a -> Bool
millerRabinOnce n d s a
  | even n           = False
  | otherwise        = not (test1 && test2)
  where
    (d,s) = findDS n

    test1 = powerModulo a d n /= 1
    test2 = and $ map (\t -> powerModulo a ((2^t)*d) n /= n-1) 
                      [0..s-1]

-- | millerRabin k n does k MR rounds testing n for primality.
millerRabin :: (RandomGen g, Random a, Integral a) =>
  a -> a -> State g Bool
millerRabin k n = millerRabin' k
  where
    (d, s)          = findDS n
    millerRabin' 0 = return True
    millerRabin' k = do
      rest <- millerRabin' $ k - 1
      test <- randomR_st (1, n - 1)
      let this = millerRabinOnce n d s test
      return $ this && rest

-- | primeK k n. Probabilistic primality test of n
-- using k Miller-Rabin rounds.
primeK :: (Integral a, Random a, RandomGen g) => 
  a -> a -> State g Bool
primeK k n
  | n < 2            = return False
  | n == 2 || n == 3 = return True
  | otherwise        = millerRabin (min n k) n

-- | Probabilistic primality test with 64 Miller-Rabin rounds.
prime :: (Integral a, Random a, RandomGen g) => 
  a -> State g Bool
prime = primeK 64

问题是,无论我需要使用素数,我都必须将该函数转换为monadic函数。 即使它似乎没有涉及任何随机性。 例如,下面是我回收Shamir的秘密共享方案秘密前者功能。 确定性的操作,对吗?

recover :: Integral a => [a] -> [a] -> a -> a
recover pi_s si_s q = sum prods `mod` q
  where
    bi_s  = map (beta pi_s q) pi_s
    prods = zipWith (*) bi_s si_s

那就是我使用了一个天真的,确定性的素性测试函数。 我还没有重写recover功能,但我已经知道beta函数依赖于素数,因此它也会recover 并且两者都必须从简单的非monadic函数转变为两个monadic函数,即使它们使用State Monad / randomness的原因实际上是深层次的。

我不能不认为所有的代码变得更加复杂,因为它必须是monadic。 我是否遗漏了某些东西,或者在Haskell中这种情况总是如此?

我能想到的一个解决方案是

prime' n = runState (prime n) (mkStdGen 123)

并使用prime'代替。 该解决方案提出了两个问题。

  1. 这是一个坏主意吗? 我认为它不是很优雅。
  2. 从monadic代码到非monadic代码的“切割”应该在哪里? 因为我也有像这个genPrime功能:

_

genPrime :: (RandomGen g, Random a, Integral a) => a -> State g a
genPrime b = do
  n  <- randomR_st (2^(b-1),2^b-1)
  ps <- filterM prime [n..]
  return $ head ps

问题在于是否在genPrime之前或之后进行“切割”等。

这确实是对monads的有效批评,因为它们是在Haskell中实现的。 在短期内我没有看到比你提到的更好的解决方案,并且将所有代码转换为monadic风格可能是最强大的,即使它们比自然风格更重要,实际上它可能是一种痛苦移植大型代码库,虽然如果你想增加更多的外部效果,它可能会稍后付清。

我认为代数效应可以优雅地解决这个问题,例如:

所有函数都注释了它们的效果a -> eff b ,但是,与Haskell相反,它们都可以像纯函数a -> b一样组成(因此它们是有效函数的特殊情况,具有空效果签名)。 然后,语言确保效果形成半格子,从而可以组成具有不同效果的函数。

在Haskell中使用这样的系统似乎很困难。 Free(r)monad库允许以类似的方式组合类型的效果,但仍然需要术语级别的显式monadic样式。 一个有趣的想法是重载函数应用程序,因此它可以隐式更改为(>>=) ,但这样做的原则性方法使我无法理解。 主要问题是函数a -> mb被视为在m和codomain b具有效果的有效函数,以及作为具有codomain mb的纯函数。 我们如何推断何时使用($)(>>=)

在随机性的特殊情况下,我曾经有过一些涉及可拆分随机生成器(无耻插件)的相关想法: https//blog.poisson.chat/posts/2017-03-04-splittable-generators.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM