直观地说,在我看来,可以使用状态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的结果,以便将它传递给作为遍历的第一个参数给出的函数。这不可能吗?
答案 0 :(得分:8)
要实现Traversable t
,您需要实现此类型的函数:
sequenceA :: forall f a. Applicative f => t (f a) -> f (t a)
当t
为State s
时,会变为:
sequenceA :: forall f a. Applicative f => State s (f a) -> f (State s a)
显然,我们无法为所有s
编写实现,因为我们必须知道如何创建s
或{ {1}}无中生有地继续前进。那将是一个矛盾。
但是可以为某些类f a
编写满足类型的实例。如果我们假设s
是s
,我们可以这样做:
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
runState
和True
处的原始操作,将结果存储在地图中,然后让结果状态操作执行查找那张地图。这适用于任何有限可枚举的状态域。有关详细信息,请参阅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)
。但是,我认为这是不正确的,因为:
mempty
来折叠任何东西。Foldable
,可以轻松地将每种数据类型设为mempty
的实例。生成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) ]
因为结果列表总是具有相同的长度。