[英]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.