sch*_*uch 6 haskell applicative
尝试例子了一会儿,对我之后,它看起来像myFunction <$>
和pure myFunction <*>
在Control.Applicative型类时是等价的。
例子:
(++) <$> Just "foo" <*> Just "bar"
pure (++) <*> Just "foo" <*> Just "bar"
Run Code Online (Sandbox Code Playgroud)
两者产量Just "foobar"
。
这些确实是等效的还是我忽略了边缘情况?我更喜欢哪种变体/更常见?虽然该pure
方法更长,但对我来说,它对于 Control.Applicative 类型类来说看起来更通用和更真实。
Wil*_*sem 10
这需要等价。事实上,在Applicative
typeclass的文档中,我们读到:
作为这些定律的结果, 的
Functor
实例f
将满足Run Code Online (Sandbox Code Playgroud)fmap f x = pure f <*> x
由于(<$>) :: Functor f => (a -> b) -> f a -> f b
是:
的中缀同义词
fmap
。
因此认为:
f <$> x = pure f <*> x
Run Code Online (Sandbox Code Playgroud)
因此,可以使用两者来实现相同的目的。f <$> x
然而,由于更短,并且可能更快(因为(<*>)
需要处理所有f a
s),因此可能建议使用(<$>)
)。此外,正如@chepner 所说,默认实现liftA2
是liftA2 f x = (<*>) (fmap f x)
,所以这也使用fmap
(so <$>
)。
我只想在这里简单地插一句,因为我持有许多不受欢迎的意见,这就是其中之一,你这么问了 nyaaaaah。
承认:社区似乎或多或少同意f <$> x <*> y <*> z
风格更好。但我实际上更喜欢pure f <*> x <*> y <*> z
. 我发现以 applicative 风格编写的行往往比较长,因为每个参数本身通常都是对函数的调用,因此:
fancyParser = FancyConstructor <$> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
-- OR
fancyParser = pure FancyConstructor <*> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
Run Code Online (Sandbox Code Playgroud)
为了可读性,我经常将论点拆分成自己的行;我发现视觉分离使参数边界的位置更清晰,让我的眼睛休息一下,并且不太可能以丑陋的方式包裹线:
fancyParser = FancyConstructor
<$> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
-- OR
fancyParser = pure FancyConstructor
<*> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
Run Code Online (Sandbox Code Playgroud)
在这种形式中,我认为很清楚为什么我更喜欢pure
/ <*>
:它使行开始完全一致。一方面,这种一致性在视觉上很吸引人。但更重要的是,当我不可避免地重构FancyConstructor
以重新排列字段的顺序,或者在开头插入一个新字段时,我可以完全不受惩罚地使用逐行编辑命令。某些正则表达式搜索也变得稍微容易一些。它只是消除了一点点摩擦……但消除小摩擦是进行大型编程的一个重要部分。
PS其他不受欢迎的获得一致行格式的方法示例,我发现它们很方便:
-- orthodox
foo a b c d
= f
. g
. h
-- dmwit
foo a b c d = id
. f
. g
. h
-- orthodox
foo a b c d =
[ x
, y
, z
]
-- dmwit
foo a b c d = tail [undefined
, x
, y
, z
]
-- orthodox
bad = die
$ "some kind of "
<> "error that is best described "
<> "on multiple lines"
-- dmwit
bad = die $ mempty {- or "", if appropriate -}
<> "some kind of "
<> "error that is best described "
<> "on multiple lines"
-- orthodox
foo
:: arg1
-> arg2
-> result
-- dmwit
foo ::
arg1 ->
arg2 ->
result
-- orthodox
foo a b c d =
[ t
| u <- v
, w <- x
, y <- z
]
-- dmwit
foo a b c d =
[ t | _ <- [()]
, u <- v
, w <- x
, y <- z
]
Run Code Online (Sandbox Code Playgroud)