当我正在学习我的考试功能编程时,我仍然试图真正理解 Monads。还有什么比自己定义一个更好的方法?我定义了这个:
newtype ST a = ST (State -> ([a], State))
type State = StdGen
基本上是List Monad和Random Monad合二为一。
这个monad应该可以让你使用随机函数和列表。
现在遇到了麻烦,因为我能够定义return
函数,但>>=
并没有完全解决问题。
instance Monad ST where
return a = ST $ \g -> ([a], g)
-- M a -> (a -> M b) -> M b
st >>= f = ST $ \s ->
let (x,s') = st s
in (f x) s'
代码的灵感来自This paper p.218
有任何解释吗?
答案 0 :(得分:5)
让我们小心跟踪所有类型(当我编写棘手的代码时,我自己这样做)。首先,让我们为您的ST类型添加一个访问器,这将使事情变得更容易。
newtype ST a = ST { runST :: State -> ([a], State) }
现在我们有runST :: ST a -> State -> ([a], State)
。在定义monad代码时,我希望立即将runST
应用于所有ST
值,因此我知道我正在使用的是哪种类型。
st >>= f = ST $ \s ->
-- runST st :: State -> ([a], State)
-- f :: a -> ST b
-- runST . f :: a -> State -> ([b], State)
-- s :: State
let (as, s') = runST st s in -- I renamed x to as for readability
-- as :: [a]
-- s' :: State
现在我们需要一个([b], State)
。我们可以使用b
获取f
。我们有一个a
的列表,所以让我们尝试映射
-- map (runST . f) as :: [State -> ([b], State)]
嗯,这不太有用,让我们尝试应用我们进来的状态:
-- map (\a -> runST (f a) s) as :: [([b], State)]
也许我们可以解决这个问题。我们列出了b
以及其他一些内容。我们将此rs
命名为("结果"):
let rs = map (\a -> runST (f a) s) as in
现在我们可以通过连接所有结果b来获取b
的列表:
let bs = concat (map fst rs) in -- bs :: [b]
所以这可能是我们想要回归的。现在哪个State
我们想要用它?我们遇到了问题,因为我们有很多不同的State
可供选择。我们选择列表中的最后一个,还是第一个?如果列表是空的,我们只需返回进来的State
。这些是任意选择 - 正如我的物理学教授曾经说过的那样:"现在我们必须做出选择,这是一个问题,因为我们要犯错误的一个"。这在函数式编程中非常正确;无论何时你必须做出像这样的任意选择,你可能会搞砸了。
如果我们退后一步并直观地思考其含义,ST a
计算将采用一种状态并返回一个选项列表,其中包含一个新状态以供将来计算,每个> 将生成一个选择列表和一个新状态。我们可以使用concat
将所有列表组合在一起,但我们没有办法将所有状态组合成一个。使用随机API我们没有这个(我们可以想象可能bitxor
在一起所有的状态......)。
如果没有一种结合状态的方法,我们的monad bind将不得不忘记它拥有的大部分数据,这是一个不好的标志,它不会遵守法律(尽管有一个monad这个复杂的我担心证明法律的复杂性)。据我所知,这种类型没有monad。
是一个此类型的monad:
newtype ST' a = ST' { runST' :: State -> [(a, State)] }
它相当于StateT State []
。现在计算的每个分支都有自己的随机状态,因此我们不需要将许多状态组合成一个状态。你可能有更好的运气来定义这个monad - 像我一样做,写下你所知道的所有类型,并尝试以类型导向的方式获得你需要的东西。尽量不要忘记"任何信息,并努力在构造输出时使用每个输入一次。
很抱歉,这篇文章有点模糊和粗暴 - 我意识到在定义monad时我使用了很多直观的原则,我想我会尝试分享它们。请记住,它不足以获得类型检查的定义(尽管这确实可以让你获得很多方法):检查法律,否则当你去使用时会发生奇怪的事情{{1符号等。
答案 1 :(得分:2)
关注luqui's lead,我们得到了
st >>= f = ST (g . runST st)
-- runST st :: State -> ([a], State)
-- f :: a -> ST b
-- runST . f :: a -> State -> ([b], State)
where
g (a:as,s) = let (bs, s2) = (runST . f) a s
(rs, sn) = g (as, s2)
in (bs ++ rs, sn)
g ([], s) = ([], s)
(未经测试)。