liftM,lilftA等之间的首选

时间:2012-03-01 01:23:01

标签: haskell

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

peek p = Vec3 <$> (#peek aiVector3D, x) p
              <*> (#peek aiVector3D, y) p
              <*> (#peek aiVector3D, z) p

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

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

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

由于只有[liftA..liftA3][liftM..liftM5]但我有几个记录超过三个或五个成员,这个问题有点复杂,所以如果我决定选择{{1}我失去了一些一致性,因为我必须为更大的记录使用不同的方法。

1 个答案:

答案 0 :(得分:25)

要记住的第一件事是,这比它应该稍微复杂一点 - 任何Monad实例具有关联的Applicative实例,以便{ {1}}和liftM函数重合。因此,这里有两个指导原则:

  • 如果您正在为任何 liftA编写通用函数,请使用Monad&amp; co。避免与仅具有liftM约束的其他函数不兼容。

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

  

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

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

一个更有效的例子是基于压缩无限流的MonadApplicative个实例 - Monad可以简单地将它们拼接在一起,而Applicative必须重新计算它扔掉的许多东西。

因此,最后一个问题是MonadliftA2 f x yf <$> x <*> y等价物。我的建议如下:

  • 如果您还要编写每个参数,请使用中缀表单,因为它对于大型表达式更容易阅读。
  • 如果您只是解除现有功能,请使用Monad而不是foo = liftA2 bar - 它会更短,更清楚地表达您正在做的事情。

最后,关于一致性的问题,如果你需要,你没有理由不能简单地定义自己的foo x y = bar <$> x <*> y等等。