Haskell 应用语言?

Haskell Applicative idiom?

我是 Haskell 的新手,对如何以最惯用和最清晰的方式最好地表达某些操作感到困惑。目前(还会有更多)我对 <*> 感到困惑(我什至不知道该称呼它)。

例如,如果我有,说

f = (^2)
g = (+10)

作为代表函数(实际上它们更复杂,但这里的关键是它们是不同的和不同的),然后

concatMap ($ [1,2,3,4,10]) [(f <$>), (g <$>) . tail . reverse] 

concat $ [(f <$>), (g <$>) . tail . reverse] <*> [[1,2,3,4,10]]

完成同样的事情。

是其中一个比较惯用的 Haskell,其中一个是否暗示了 Haskell 的 reader 经验,而另一个则没有。也许还有其他(更好)的方式来表达完全相同的事物。像我这样的新手 Haskell 可能会遗漏这两种方法之间的概念差异吗?

就你个人而言,我会写

f = (^2)
g = (+10)

let xs = [1,2,3,4,10]
in (map f xs) ++ (map g . tail $ reverse xs)

在一个非常有应用性的"mood"中,我会将in之后的部分替换为

((++) <$> map f <*> map g . tail . reverse) xs

实际上我不认为在这种情况下更具可读性。如果您不直接理解它的含义,请花一些时间了解 Applicative 实例 ((->) a) (Reader).

我认为选择实际上取决于您要尝试做什么,即您的输出应该意味着什么。在您的示例中,任务非常抽象(基本上只是展示 Applicative 可以做什么),因此使用哪个版本并不直接明显。

[]Applicative 实例直观地与组合相关,所以我会在这样的情况下使用它:

-- I want all pair combinations of 1 to 5
(,) <$> [1..5] <*> [1..5]

如果您有很多函数,并且想尝试这些函数与多个参数的所有组合,我确实会使用 Applicative[] 实例。但是如果你想要的是不同转换的串联,我会这样写(我在上面做了)。

我作为中等经验的 Haskeller 的 2 美分。

我有时会遇到类似的问题。你有一个元素,但有多个功能。

通常我们有多个元素和单一功能:所以我们这样做:

map f xs

但这不是 Haskell 中的问题。对偶很简单:

map ($ x) fs

事实上,你的 x 实际上是一个列表,你想 concatmap 之后,所以你

concatMap ($ xs) fs

我无法真正直接理解第二个方程式中发生了什么,即使我可以使用适用定律推断它与第一个方程式相同。

您的函数 (f <$>)(g <$>).tail.reverse return 都是幺半群类型(在本例中为列表),因此您可以使用 mconcat 将它们转换为单个函数。然后您可以将此函数直接应用于输入列表,而不是将其包装在另一个列表中并使用 concatMap:

mconcat [(f <$>), (g <$>).tail.reverse] $ [1,2,3,4,10]

为了扩展这一点,函数 a -> bMonoid 的一个实例,如果 b 是一个幺半群。此类函数的 implementation of mappend 是:

mappend f g x = f x `mappend` g x

或等同于

mappend f g = \x -> (f x) `mappend` (g x)

所以给定两个函数 fg 其中 return 一个幺半群类型 b, fmappendg return 是一个函数,它将其参数应用于 fg,并使用 bMonoid 实例组合结果。

mconcat 具有类型 Monoid a => [a] -> a 并使用 mappend.

组合输入列表的所有元素

列表是幺半群,其中 mappend == (++) 所以

mconcat [(f <$>), (g <$>).tail.reverse]

return是一个类似于

的函数
\x -> (fmap f x) ++ (((fmap g) . tail . reverse) x)