[英]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'
代替。 该解决方案提出了两个问题。
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风格可能是最强大的,即使它们比自然风格更重要,实际上它可能是一种痛苦移植大型代码库,虽然如果你想增加更多的外部效果,它可能会稍后付清。
我认为代数效应可以优雅地解决这个问题,例如:
eff ( 具有随机性的示例程序 )
所有函数都注释了它们的效果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.