在 Haskell 中包含 Random 和 List 的状态 Monad
State Monad containing Random and List in Haskell
在我学习函数式编程考试时,我仍在努力真正理解 Monad。还有什么比自己定义更好的方法呢?我这样定义:
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
有什么解释吗?
让我们小心地跟踪所有类型(我在编写棘手的代码时自己这样做)。首先让我们为您的 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)
。我们可以通过使用f
得到b
s。我们有一个 a
的列表,所以让我们尝试映射
-- map (runST . f) as :: [State -> ([b], State)]
嗯,这不是很有用,让我们也尝试应用我们进入的状态:
-- map (\a -> runST (f a) s) as :: [([b], State)]
也许我们可以解决这个问题。我们有一个 b
列表的列表,以及一些其他的东西。我们将其命名为 rs
(对于 "results"):
let rs = map (\a -> runST (f a) s) as in
现在我们可以通过连接所有结果 bs:
来得到 b
s 的列表
let bs = concat (map fst rs) in -- bs :: [b]
所以这大概就是我们想要的 return。现在我们要选择哪个State
?我们遇到了一个问题,因为我们有很多不同的 State
可供选择。我们是选择列表中的最后一个,还是第一个?如果列表是空的,也许我们只是 return 进来的 State
。这些是任意选择——正如我的一位物理学教授曾经说过的:"now we have to make a choice, which is a problem, because we're about to make the wrong one"。这在函数式编程中非常正确。每当您必须像这样做出任意选择时,您可能就搞砸了。
如果我们退后一步,直观地思考其含义,ST a
计算需要一个状态,return 是一个具有新状态的选项列表,可用于未来的计算,其中每一个 都会产生一个选项列表和一个新状态。我们可以使用 concat
将所有列表组合在一起,但我们没有办法将所有状态组合在一起。对于随机 API 我们没有这个(我们可以想象也许 bitxor
将所有状态放在一起......)。
如果没有办法组合状态,我们的 monad 绑定将不得不忘记它拥有的大部分数据,这是一个很好的迹象,表明它不会遵守法律(尽管我担心 monad 这么复杂证明定律的复杂性)。据我所知,没有这种类型的 monad。
有是一个monad这个类型:
newtype ST' a = ST' { runST' :: State -> [(a, State)] }
并且相当于StateT State []
。现在计算的每个分支都有自己的随机状态,所以我们不再需要将许多状态组合成一个。定义这个 monad 可能运气更好——像我做的那样,写下你知道的所有类型,并尝试以类型导向的方式获得你需要的东西。尽量不要 "forget" 任何信息,并力求在构造输出时每个输入只使用一次。
抱歉,这个 post 有点含糊不清 -- 我意识到我在定义 monad 时使用了很多直观的原则,我想我会尝试分享它们。请记住,仅仅获得类型检查的定义是不够的(尽管这确实让你在那里有很多方法):也要检查法律,否则当你使用 do
符号时你会发生奇怪的事情并且之类的
在之后,我们得到
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)
(未测试)。
在我学习函数式编程考试时,我仍在努力真正理解 Monad。还有什么比自己定义更好的方法呢?我这样定义:
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
有什么解释吗?
让我们小心地跟踪所有类型(我在编写棘手的代码时自己这样做)。首先让我们为您的 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)
。我们可以通过使用f
得到b
s。我们有一个 a
的列表,所以让我们尝试映射
-- map (runST . f) as :: [State -> ([b], State)]
嗯,这不是很有用,让我们也尝试应用我们进入的状态:
-- map (\a -> runST (f a) s) as :: [([b], State)]
也许我们可以解决这个问题。我们有一个 b
列表的列表,以及一些其他的东西。我们将其命名为 rs
(对于 "results"):
let rs = map (\a -> runST (f a) s) as in
现在我们可以通过连接所有结果 bs:
来得到b
s 的列表
let bs = concat (map fst rs) in -- bs :: [b]
所以这大概就是我们想要的 return。现在我们要选择哪个State
?我们遇到了一个问题,因为我们有很多不同的 State
可供选择。我们是选择列表中的最后一个,还是第一个?如果列表是空的,也许我们只是 return 进来的 State
。这些是任意选择——正如我的一位物理学教授曾经说过的:"now we have to make a choice, which is a problem, because we're about to make the wrong one"。这在函数式编程中非常正确。每当您必须像这样做出任意选择时,您可能就搞砸了。
如果我们退后一步,直观地思考其含义,ST a
计算需要一个状态,return 是一个具有新状态的选项列表,可用于未来的计算,其中每一个 都会产生一个选项列表和一个新状态。我们可以使用 concat
将所有列表组合在一起,但我们没有办法将所有状态组合在一起。对于随机 API 我们没有这个(我们可以想象也许 bitxor
将所有状态放在一起......)。
如果没有办法组合状态,我们的 monad 绑定将不得不忘记它拥有的大部分数据,这是一个很好的迹象,表明它不会遵守法律(尽管我担心 monad 这么复杂证明定律的复杂性)。据我所知,没有这种类型的 monad。
有是一个monad这个类型:
newtype ST' a = ST' { runST' :: State -> [(a, State)] }
并且相当于StateT State []
。现在计算的每个分支都有自己的随机状态,所以我们不再需要将许多状态组合成一个。定义这个 monad 可能运气更好——像我做的那样,写下你知道的所有类型,并尝试以类型导向的方式获得你需要的东西。尽量不要 "forget" 任何信息,并力求在构造输出时每个输入只使用一次。
抱歉,这个 post 有点含糊不清 -- 我意识到我在定义 monad 时使用了很多直观的原则,我想我会尝试分享它们。请记住,仅仅获得类型检查的定义是不够的(尽管这确实让你在那里有很多方法):也要检查法律,否则当你使用 do
符号时你会发生奇怪的事情并且之类的
在
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)
(未测试)。