【问题标题】:Unsequence Monad function within HaskellHaskell中的Unsequence Monad函数
【发布时间】:2014-12-07 13:04:06
【问题描述】:

我在设计 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 [] 时我需要一个断点,但并不是所有的单子都有Eq 实例,那么我该怎么做呢?这甚至可能吗?如果是这样,为什么和为什么不呢?请告诉我。

【问题讨论】:

  • 要了解为什么它不能在一般情况下完成,请考虑类似 do { b <- check; if b then list1 else list2 } :: M [A] 的一些具体 monad M 和一些具体类型 A

标签: list function haskell monads reverse


【解决方案1】:

您不能拥有unsequence :: (Monad m) => m [a] -> [m a]。问题在于列表:您无法确定列表将包含多少元素,这会使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 函子是另一个。但不是传统的列表,也不是像 MaybeEither 这样的函子。

distributive 包中,有一个名为Distributive 的类型类封装了这个属性。你的unsequence 在那里被称为distribute

【讨论】:

    【解决方案2】:

    确实不可能单独使用 monad 来创建 unsequence 函数。原因是:

    1. 您可以使用 return 从值安全轻松地创建一元结构。
    2. 但是,从一元结构中删除值是不安全的。例如,您不能从空列表中删除元素(即 [a] -> a 类型的函数不安全)。
    3. 因此我们有一个特殊的函数(即>>=),它可以安全地从一元结构中删除一个值(如果存在)、处理它并返回另一个安全的一元结构。

    因此,从一个值创建一元结构是安全的。但是,从一元结构中删除值是不安全的。

    假设我们有一个函数extract :: Monad m => m a -> a,它可以“安全地”从一元结构中删除一个值。然后我们可以如下实现unsequence

    unsequence :: Monad m => m [a] -> [m a]
    unsequence = map return . extract
    

    但是,从一元结构中提取值没有安全的方法。因此unsequence []unsequence Nothing 将返回undefined

    但是,您可以为单子和共子的结构创建unsequence 函数。 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
    

    一元结构与一元结构相反。特别是:

    1. 您可以安全地从单节结构中提取值。
    2. 但是,您不能从一个值安全地创建一个新的comondic 结构,这就是duplicate 函数从一个值安全地创建一个新的comondic 结构的原因。

    还记得unsequence 的定义需要returnextract 吗?您不能从一个值安全地创建一个新的 comonadic 结构(即,comonadic 结构没有return)。因此unsequence函数定义如下:

    unsequence :: (Comonad m, Monad m) => m [a] -> [m a]
    unsequence = map return . extract
    

    有趣的是,sequence 只适用于简单的一元结构。因此,通过直觉,您可能会假设 unsequence 适用于简单的共元结构。但事实并非如此,因为您需要先从单子结构中提取列表,然后将列表中的每个元素放入单子结构中。

    unsequence 函数的通用版本将一个comonadic 列表结构转换为monadic 结构的列表:

    unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
    unsequence = map return . extract
    

    另一方面,sequence 函数仅适用于单子结构,因为您只是通过链接所有单子将单子结构列表折叠成单子列表结构:

    import Control.Monad (liftM2)
    
    sequence :: Monad m => [m a] -> m [a]
    sequence = foldr (liftM2 (:)) (return [])
    

    希望对您有所帮助。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    相关资源
    最近更新 更多