【问题标题】:Defining fmap with id and return用 id 定义 fmap 并返回
【发布时间】:2018-11-16 08:55:21
【问题描述】:

在 Bartosz Milewski 的程序员类别理论中,Milewski 编写了以下代码来定义 Writer monad 的 return 和“鱼”运算符(Kleisli 类别中的组合)。

return :: a -> Writer a
return x = (x, "")

(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
m1 >=> m2 = \x ->
  let (y, s1) = m1 x
      (z, s2) = m2 y
  in  (z, s1 ++ s2)

然后他继续定义fmap如下:

fmap f = id >=> (\x -> return (f x))

我很难理解这里如何使用id 函数。 fish 运算符的第一个参数显然是 (a -> Writer b),但 id 具有类型签名 a -> a

这是我理解的错误还是缺陷?将id 替换为return 对我来说更有意义。

【问题讨论】:

    标签: haskell functional-programming monads functor


    【解决方案1】:

    不要忘记普遍的量化。

    Fish (>=>) 具有 (a -> Writer b) -> ..... 类型 任何 ab

    id 具有 a -> a 类型 对于任何 a

    因此,特别是对于任何b,fish 也具有类型(Writer b -> Writer b) -> ...(只需将a = Writer b 作为特例)。

    此外,id 还具有类型 Writer b -> Writer b(同样,作为一种特殊情况)。

    这里的“技巧”是使用unification“合并”这两种类型。 我们首先要求(a -> Writer b) = (a' -> a'),然后我们推断a = a'Writer b = a'。从这里可以看出,这两种类型是可以统​​一的,所以传递参数没有矛盾。

    (另请注意,这里我们将id 类型中的a 重命名为a',以避免与鱼的其他a 混淆)

    【讨论】:

    • 谢谢你,这有帮助。只是一个额外的问题,如何创建这样的无点函数?我在思考没有参数的函数时遇到了困难。
    • @MohideenImranKhan 在一般情况下,这并不容易。人们通常会尝试专门化手头的多态值,给出不太通用的类型,然后以某种方式组合它们。此外,可以从一个有针对性的定义开始,然后使用像 [this](pointfree.io) 这样的自动工具来删除这些点。但是,使用该工具我们很少能获得可读的代码。
    • @MohideenImranKhan:一般来说,在编写无点代码时,最简单的方法是将问题分解为许多小的无点定义,并将它们与组合或更高级别的组合器(如 @987654340)结合起来@/<*>/=<< 来自FunctorApplicativeMonad(->) a(函数/读取器)实例,或来自Control.Arrow 的组合子,例如(&&&)(***)。你也可以从一个点函数开始,看看它的任何部分是否与(f . g) x = f (g x)(f <*> g) x = f x (g x) 等组合子的形状相匹配,然后逐步将其重构为无点。
    • 我明白了。当构造和解释变得更难时,为什么人们更喜欢无点函数?
    • @MohideenImranKhan Point-free 并不是在任何情况下都被认为是一种好的风格。有时它很好,很优雅。但是,在许多情况下,它会使代码更难阅读,因此不应该使用它。当它无法简化代码时,无点样式被戏称为“无点样式”,因为它变成了徒劳的练习。也许程序员在达到无点形式时觉得很聪明,并想向世界展示它,但展示不可读的代码并不是展示程序员技能的好方法。如果您不确定它是否简化,请避免使用无点代码。
    猜你喜欢
    • 2013-12-11
    • 2016-11-11
    • 1970-01-01
    • 2012-05-21
    • 2014-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多