簡體   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