为什么州monad不可穿越?

时间:2015-11-16 13:25:14

标签: haskell

直观地说,在我看来,可以使用状态monad的遍历,例如:

traverse (\a -> [a, a+1]) (state (\s -> (1, s + 1))) 
  = [state (\s -> (1, s + 1), state (\s -> (2, s + 1)]

但是状态monad没有实现Traversable类型类。这是为什么?我试图找出一种方法来实现状态monad的遍历,似乎难以提取状态monad的结果,以便将它传递给作为遍历的第一个参数给出的函数。这不可能吗?

4 个答案:

答案 0 :(得分:8)

要实现Traversable t,您需要实现此类型的函数:

sequenceA :: forall f a. Applicative f => t (f a) -> f (t a)

tState s时,会变为:

sequenceA :: forall f a. Applicative f => State s (f a) -> f (State s a)

显然,我们无法为所有s 编写实现,因为我们必须知道如何创建s或{ {1}}无中生有地继续前进。那将是一个矛盾。

但是可以为某些类f a 编写满足类型的实例。如果我们假设ss,我们可以这样做:

Monoid

但这符合instance (Monoid m) => Traversable (State m) where sequenceA (State run) = fmap (\a -> State (\s' -> (mappend s s', a)) fa where (s, fa) = run mempty 法律。其中一条法律是:

Traversable

(回想一下traverse Identity = Identity

这个法律显然不会成立,因为输出动作traverse f = sequence . fmap f而输入动作没有。好的,我们只是不要mappend

mappend

这没有用,因为现在输出操作会忽略其输入并替换instance (Monoid m) => Traversable (State m) where sequenceA (State run) = fmap (\a -> State (\_ -> (s, a)) fa where (s, fa) = run mempty ,而输入操作则没有。{/ p>

这并不意味着我们无法构建合法mempty实例的s类型。例如,Traverse (State s)会缩减为State (),这绝对是可以遍历的。

如果我们可以Identity,为什么不State ()?我们只需State Bool runStateTrue处的原始操作,将结果存储在地图中,然后让结果状态操作执行查找那张地图。这适用于任何有限可枚举的状态域。有关详细信息,请参阅this answer

答案 1 :(得分:5)

成为Traversable需要Foldable;但是State monad不是 - 你不能foldMap它,因为它的价值很简单,不在这里。

type State s = StateT s Indentity
newtype StateT s m a = State { runState :: s -> m (s, a) }

正如您所看到的,此处没有立即a折叠,这是该函数的唯一结果。

答案 2 :(得分:2)

是的,这确实是不可能的。请考虑State的以下定义:

newtype State s a = State { runState :: s -> (a, s) }

extract这个数据类型的值,我们首先需要提供一些状态。

如果我们知道状态的类型,我们可以创建一个专门的extract函数。例如:

extract' :: State () a -> a
extract' (State f) = f ()

extractT :: State Bool a -> a
extractT (State f) = f True

extractF :: State Bool a -> a
extractF (State f) = f False

但是,我们无法创建通用提取功能。例如:

extract :: State s a -> a
extract (State f) = f undefined

上述extract函数是通用的。我们能提供的唯一国家是⊥,这是不正确的。只有函数f :: s -> (a, s)透明地传递其输入(即f = (,) a某个值a)时才是安全的。但是,f可能需要一些状态并使用它来生成一些值和一个新状态。因此,f可以非透明地使用其输入,如果输入为⊥,则会出现错误。

因此,我们无法为extract数据类型创建通用State函数。

现在,要使数据类型成为Traversable的实例,它首先需要是Foldable的实例。因此,要使State成为Traversable的实例,我们首先需要定义以下实例:

instance Foldable (State s) where
    foldMap f (State g) = mempty
    -- or
    foldMap f (State g) = let x = f (extract g) in mconcat [x]
    -- or
    foldMap f (State g) = let x = f (extract g) in mconcat [x,x]
    -- or
    foldMap f (State g) = let x = f (extract g) in mconcat [x,x,x]
    -- ad infinitum

请注意,foldMap的类型为Monoid m => (a -> m) -> State s a -> m。因此,表达式foldMap f (State g)必须返回类型Monoid m => m的值。通过定义mempty,我们总是可以返回foldMap = const (const mempty)。但是,我认为这是不正确的,因为:

  1. 我们并不总是通过返回mempty来折叠任何东西。
  2. 通过始终返回Foldable,可以轻松地将每种数据类型设为mempty的实例。
  3. 生成Monoid m => m类型值的唯一方法是将f应用于x类型的某个值a。但是,我们没有a类型的任何值。如果我们可以从extract a State s a,那么我们可以将f应用于该值,但我们已经证明不可能定义通用extract从不崩溃的State s a函数。

    因此,State s不能成为Foldable的实例,因此它不能是Traversable的实例。

答案 3 :(得分:0)

这样的功能不存在。

您要求使用此签名的函数:

trav :: (a -> [a]) -> (s -> (a,s)) -> [ s -> (a,s) ]

即,给定函数f :: a -> [a]和状态计算,trav返回列表。特别是,如果我给你f和状态计算,你应该能告诉我结果列表是否为空。

现在考虑这个f

f :: Int -> [Int]
f 0 = []
f a = [a]

尝试评估trav f (\s -> (s,s)),或者甚至尝试确定它是否为空列表。

你可以写这个函数:

trav' :: [ a -> a ] -> (s -> (a,s)) -> [ s -> (a,s) ]

因为结果列表总是具有相同的长度。