【问题标题】:Function as an Instance of Monad作为 Monad 实例的函数
【发布时间】:2012-10-16 07:27:59
【问题描述】:

我很难理解函数如何成为 monad。

根据Control.Monad.Instances 中的声明,函数(->) r 是一个monad:

instance Monad ((->) r) where  
    return x = \_ -> x  
    h >>= f = \w -> f (h w) w  

即使 Miran Lipovača says 对此感到困惑:

>>= 的实现似乎有点神秘,但实际上并非如此 所有这一切。当我们使用>>= 将一元值提供给函数时, 结果始终是一元值。所以在这种情况下,当我们喂一个 函数到另一个函数,结果也是一个函数。那是 为什么结果以 lambda 开始。的所有实现 >>= 到目前为止,总是以某种方式将结果与一元值隔离,然后将函数 f 应用于该结果。同样的事情发生 这里。要从函数中获取结果,我们必须将其应用于 一些东西,这就是为什么我们在这里使用(h w) 来获取结果 函数,然后我们将 f 应用于该函数。 f 返回一个单子值,它 在我们的例子中是一个函数,所以我们也将它应用到 w 上。

(>>=) 的类型签名是这样的: (>>=) :: m a -> (a -> m b) -> m b

所以我认为h 的类型为m af 的类型为(a -> m b)。如果函数是m a,它是否返回a 类型值?还是返回其他采用a 类型的东西?

如果h 的非monad 值被馈送到f,那么我们得到: f (h w) 看起来不错。因为f 是一个函数并且接受了它的唯一参数,所以它已经是一个值了,不是吗?由于它是一元函数,因此该值也是一元值。那么为什么它需要另一个值w?将w 喂给f something 不会使它成为非单子的,也就是说,它不再是一个函数,不是吗?我也无法理解为什么f somethingh 采用相同的参数w 并返回不同的值类型(m am b)。

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    通过查看 join 的作用可能更容易理解这个 monad,因为可以等效地使用 fmapjoin 而不是 >>= 来定义 monad。

    join 的一般形式具有Monad m => m (m b) -> m b 类型,因此它采用“两层”一元值并将其分解为一层。

    使用函数 monad,m ~ (a ->),所以 join 的类型为 (a -> a -> b) -> (a -> b),所以它接受一个有两个参数的函数并返回一个只接受一个参数的函数。

    join :: (a -> a -> b) -> (a -> b)
    join f = \x -> f x x
    

    如您所见,它只是重复了参数。

    同样,函数上的fmap 只是函数组合,而returnconst

    我认为用这种方式理解比试图理解 >>= 更容易。

    【讨论】:

    • 虽然这对于从概念上理解实例很有用,但您仍然必须理解 >>= 的行为,以便了解 do 符号中的每一行对于该 monad 的含义。
    • @DanBurton:在这种情况下,我实际上认为Applicative 实例是最容易解释和理解的,因为“环境”始终是第一个参数,而Monad 实例相当于参数顺序。
    【解决方案2】:

    首先,这是(>>=)的类型:

    (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
    

    现在,m 专门用于((->) r)

    (>>=) :: ((->) r) a -> (a -> ((->) r) b) -> ((->) r) b
    

    用所有功能箭头中缀重写:

    (>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
    

    去掉一些多余的括号:

    (>>=) :: (r -> a) -> (a -> r -> b) -> r -> b
    

    此时应该更容易看到发生了什么:将第三个参数(r 类型)赋予第一个参数以获取 a 类型的东西,然后是该结果和第三个参数被赋予第二个参数以获得b 类型的最终结果。

    因此,((->) r) 作为Monad 表示该 monad 中每个值的额外函数参数,当组合 monadic 值时,单个“额外”参数被复制并赋予每个输入值。从本质上讲,这为一元值创建了一个“只读全局环境”。这种解释以Reader monad 的形式明确提供,它只是((->) r) 的包装。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多