[英]Unsequence Monad function within Haskell
我在设计Haskell sequence
函数的反函数时遇到了一些麻烦,Hoogle告诉我这个函数还不存在。 这是它的行为方式:
ghci> sequence [Just 7, Just 8, Just 9]
Just [7,8,9]
ghci> sequence [getLine, getLine, getLine]
hey
there
stack exchange
["hey","there","stack exchange"] :: IO [String]
我的问题是制作这样的函数:
unsequence :: (Monad m) => m [a] -> [m a]
所以它的行为如下:
ghci> unsequence (Just [7, 8, 9])
[Just 7, Just 8, Just 9]
ghci> sequence getLine
hey
['h','e','y'] :: [IO Char] --(This would actually cause an error, but hey-ho.)
我实际上并不知道这是否可能,因为我会在某个时候逃避monad,但我已经开始了,虽然我不知道如何为这个递归函数设置断点:
unsequence m = (m >>= return . head) : unsequence (m >>= return . tail)
我知道当这里的m
等于return []
时我需要一个断点,但并不是所有monad都有Eq
实例,所以我怎么能这样做呢? 这甚至可能吗? 如果是这样,为什么不呢? 请告诉我。
你不能没有unsequence :: (Monad m) => m [a] -> [ma]
。 问题在于列表:您无法确定列表中的元素是如何获得的,这会使任何合理的unsequence
定义变得复杂。
有趣的是,如果你是绝对的, 100%确定monad中的列表是无限的,你可以这样写:
unsequenceInfinite :: (Monad m) => m [a] -> [m a]
unsequenceInfinite x = fmap head x : unsequenceInfinite (fmap tail x)
它会工作!
还想象一下我们周围有一个Pair
仿函数。 我们可以写unsequencePair
作为
unsequencePair :: (Monad m) => m (Pair a) -> Pair (m a)
unsequencePair x = Pair (fmap firstPairElement x) (fmap secondPairElement x)
一般来说,事实证明你只能为仿函数定义unsequence
,你可以随时将两个值“压缩”在一起而不会丢失信息。 无限列表(在Haskell中,一种可能的类型是Cofree Identity
)就是一个例子。 Pair
仿函数是另一个。 但不是常规列表,也不是像Maybe
或Either
这样的仿函数。
在distributive
包中,有一个名为Distributive
的类型类封装了这个属性。 你的unsequence
被称为distribute
在那里。
确实不可能单独使用monad来创建一个unsequence
函数。 原因是:
return
从值安全轻松地创建monadic结构。 [a] -> a
函数不安全)。 >>=
),它可以安全地从monadic结构中删除一个值(如果存在),处理它并返回另一个安全的monadic结构。 因此,从值创建monadic结构是安全的。 但是从monadic结构中删除值是不安全的。
假设我们有一个函数extract :: Monad m => ma -> a
,它可以“安全地”从monadic结构中删除一个值。 然后我们可以实现如下的unsequence
:
unsequence :: Monad m => m [a] -> [m a]
unsequence = map return . extract
但是,没有一种安全的方法可以从monadic结构中提取值。 因此unsequence []
和unsequence Nothing
将返回undefined
。
但是,您可以创建一个unsequence
为均为一元和comonadic结构功能。 Comonad
定义如下:
class Functor w => Comonad w where
extract :: w a -> a
duplicate :: w a -> w (w a)
extend :: (w a -> b) -> w a -> w b
duplicate = extend id
extend f = fmap f . duplicate
一个共同结构与一元结构相反。 特别是:
duplicate
函数从值安全地创建新的comonadic结构的原因。 请记住, unsequence
的定义需要return
和extract
吗? 您无法从值安全地创建新的comonadic结构(即,comonadic结构没有return
)。 因此, unsequence
函数定义如下:
unsequence :: (Comonad m, Monad m) => m [a] -> [m a]
unsequence = map return . extract
有趣的是, sequence
适用于单一结构。 所以,通过直觉,你可能会认为, unsequence
适用于只是comonadic结构。 但事实并非如此,因为您需要首先从comonadic结构中提取列表,然后将列表的每个元素放入monadic结构中。
在常规版本unsequence
函数转换comonadic列表结构一元结构的列表:
unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
unsequence = map return . extract
另一方面, sequence
函数仅适用于monadic结构,因为您只是通过链接所有monad将monadic结构列表折叠成monadic列表结构:
import Control.Monad (liftM2)
sequence :: Monad m => [m a] -> m [a]
sequence = foldr (liftM2 (:)) (return [])
希望有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.