liftM,lilftA等之间的首选

Joe*_*get 16 haskell

最近我一直在编写FFI代码,它返回IO monad中的数据结构.例如:

peek p = Vec3 <$> (#peek aiVector3D, x) p
              <*> (#peek aiVector3D, y) p
              <*> (#peek aiVector3D, z) p
Run Code Online (Sandbox Code Playgroud)

现在我可以想到编写代码的四种不错的方法,所有这些都密切相关:

peek p = Vec3 <$> io1 <*> io2 <*> io3
peek p = liftA3 Vec3 io1 io2 io3
peek p = return Vec3 `ap` io1 `ap` io2 `ap` io3
peek p = liftM3 Vec3 io1 io2 io3
Run Code Online (Sandbox Code Playgroud)

请注意,我要求的monadic代码除了Applicative提供的内容之外不需要任何其他内容.编写此代码的首选方法是什么?我应该Applicative用来强调代码的作用,或者我应该使用Monad它,因为它可能(?)有优化Applicative

现在的问题是轻微的事实,只有复杂的[liftA..liftA3][liftM..liftM5],但我有几个记录超过三个或五个成员,所以如果我决定去与lift{A,M}我失去了一些一致性,因为我将不得不使用不同的方法,对于较大的记录.

C. *_*ann 25

要记住的第一件事是,这比它应该稍微复杂一点 - 任何Monad实例都应该有一个关联的Applicative实例,使得liftMliftA函数重合.因此,这里有两条准则:

  • 如果你正在编写一个通用功能的任何 Monad使用liftM&CO.避免与仅具有Monad约束的其他函数不兼容.

  • 如果您正在使用Monad您知道具有附带Applicative实例的特定实例,请Applicative在不需要Monad操作的任何定义或子表达式中始终使用运算符,但避免漫无目的地混合它们.

我应该Applicative用来强调代码的作用,或者我应该使用Monad它,因为它可能(?)有优化Applicative

一般来说,如果存在差异,那将是相反的方式.Applicative仅支持计算的静态"结构",而Monad允许嵌入式控制流.例如,考虑列表 - Applicative您可以做的就是生成所有可能的组合并对每个组合进行转换 - 结果元素的数量完全取决于每个输入中的元素数量.使用Monad,您可以根据输入元素在每个步骤生成不同数量的元素,允许您任意过滤或扩展.

更有效的例子是在ApplicativeMonad实例基于荏苒无限streams-- Applicative可以简单地压缩在一起的明显的方式,而Monad必须重新计算很多东西,它然后扔掉.

所以,最后一个问题是liftA2 f x yvs. f <$> x <*> yMonad等价物.我的建议如下:

  • 如果你正在编写每个参数,请使用中缀表单,因为它更容易阅读大表达式.
  • 如果您只是提升现有功能,请使用foo = liftA2 bar而不是foo x y = bar <$> x <*> y- 更短,更清楚地表达您正在做的事情.

最后,在一致性问题上,没有理由你不能简单地定义自己的liftA4等等,如果你需要它们.