Haskell FMAP.FMAP功能,两个函子上的函数



我发现自己越来越经常做这样的事情...

我有一个函数 f :: IO [a],然后我想在其上应用 g :: a -> b的函数,以获取 IO [b]

很长的路是:

x <- f
let y = fmap g x

然后我缩短到:

x <- f
let y = g <$> x

,如今我宁愿做:

y <- fmap g <$> f

但随后使用Functor法律,我可以看到我可以做:

(<$$>) = fmap . fmap
y <- g <$$> f

,尽管我经常看到这种fmap . fmap提到它在基本库中没有特殊的绑定,而对我来说似乎是一个频繁的习语。从字面上看,我可以在整个地方使用它!所以现在我想知道,我是否没有足够努力地编写纯代码,这就是为什么我比经验丰富的haskell程序员击中它……或者有一种更优雅的方法来解释为什么别人不使用这个成语很多?

我是意见(在Haskell社区的一部分中似乎不受欢迎),您应该编写代码以清楚地表达您的意图并遵循读者的期望,并且不是然后,代数将代码重构为等效的形式,即"整洁",但不再清楚。

在这种情况下,似乎在这里有两次FMAP的事实似乎没有任何真正的含义。您正在调整IO操作的结果的事实并未在逻辑上归结为以下事实:调整恰好是在列表上映射的事实。如果您在代码中多次看到" fmap . fmap模式",那只是因为fmap是一个非常普遍的功能。

如果您写

    y <- g <$$> x      -- or any other made-up operator name

您不仅强迫读者学习一个不熟悉的操作员,而且一旦他们学习了它,他们还是必须将其扩展到y <- fmap g <$> x,以了解正在发生的事情,因为您将两个无关的操作分组在一起。

最好的形式是您的y <- fmap g <$> x(甚至更好,y <- map g <$> x),因为它适合熟悉的模式var <- function <$> action

(顺便说一句,您提到了函数法律,但它们与问题中的任何重写无关,这只是扩展定义。)

好吧,您拥有的是函数的组成。该概念的最广泛实现&ndash;尽管并不像您想要的那样一般性&ndash;是 monad变形金刚

http://hackage.haskell.org/package/transformers-0.4.4.3.0/docs/control-monad-monad-trans-list.html#v:listt

f' :: ListT IO a
f' = ListT f

在此上,您只需使用fmap g即可获得一个可以使用runListT的值h' :: ListT IO b,然后将其评估为IO [b]

该包装和解开是否值得取决于您连续进行的操作数量,可以使用相同的组成。对于某些应用,Monad Transformers简直太棒了。在其他情况下,它们只是使东西比现在更笨拙。fmap . fmap实际上还不错,是吗?通常我会呆在那儿。

函数(以及应用程序)组成,Compose NewType定义了两个函数组成的函数和应用实例。这意味着您可以将IO [a]包装在Compose IO [] a中并应用fmap,例如

import Data.Functor.Compose
getCompose $ fmap g $ Compose f

我发现自己也经常这样做,发现它确实令人困惑。我发现liftM . mapfmap . fmap更容易读取。

最新更新