应用程序:<$> 与 pure 和 <*>
Applicatives: <$> vs. pure and <*>
在尝试了一段时间的示例后,对我来说,在处理 Control.Applicative 类型 class.[=15 时,myFunction <$>
和 pure myFunction <*>
看起来是等价的=]
示例:
(++) <$> Just "foo" <*> Just "bar"
pure (++) <*> Just "foo" <*> Just "bar"
两者都产生 Just "foobar"
。
这些确实是等效的还是我忽略了边缘情况?
我更喜欢哪种变体/更常见?虽然 pure
方法更长,但对我来说 Control.Applicative typclass 看起来更笼统和真实。
这需要等效。事实上,在 Applicative
typeclass 的文档中,我们读到:
As a consequence of these laws, the Functor
instance for f
will
satisfy
fmap f x = pure f <*> x
因为 (<$>) :: Functor f => (a -> b) -> f a -> f b
是:
An infix synonym for fmap
.
因此认为:
f <$> x = pure f <*> x
因此可以使用两者来实现相同的目的。然而,由于 f <$> x
更短,而且可能更快(因为 (<*>)
需要处理所有 f a
),因此可能建议使用 (<$>)
。此外,作为 ,liftA2
的默认实现是 liftA2 f x = (<*>) (fmap f x)
,因此它也使用 fmap
(所以 <$>
)。
我想在这里简单地插一句,因为我有很多不受欢迎的观点,这就是其中之一,而你这么问 nyaaaaah。
承认:社区似乎或多或少同意 f <$> x <*> y <*> z
风格更好。但我其实更喜欢pure f <*> x <*> y <*> z
。我发现以应用风格编写的行往往比较长,因为每个参数本身通常是对函数的调用,所以:
fancyParser = FancyConstructor <$> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
-- OR
fancyParser = pure FancyConstructor <*> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
为了可读性,我经常将论点分成一行;我发现视觉上的分离让参数边界的位置更清晰,让我的眼睛稍微休息一下,并且减少了线条以丑陋的方式换行的可能性:
fancyParser = FancyConstructor
<$> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
-- OR
fancyParser = pure FancyConstructor
<*> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
在这种形式中,我认为我更喜欢 pure
/<*>
的原因很清楚:它使行开头完全一致。一方面,这种一致性在视觉上很吸引人。但更重要的是,当我不可避免地重构 FancyConstructor
以重新排列字段的顺序,或在开头插入一个新字段时,我可以完全不受惩罚地使用逐行编辑命令。某些正则表达式搜索也变得稍微容易一些。它只能消除一点点摩擦……但是消除小摩擦是进行大型编程的一个重要部分。
P.S。我发现方便的其他不受欢迎的获得一致行格式的方法示例:
-- 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
]
在尝试了一段时间的示例后,对我来说,在处理 Control.Applicative 类型 class.[=15 时,myFunction <$>
和 pure myFunction <*>
看起来是等价的=]
示例:
(++) <$> Just "foo" <*> Just "bar"
pure (++) <*> Just "foo" <*> Just "bar"
两者都产生 Just "foobar"
。
这些确实是等效的还是我忽略了边缘情况?
我更喜欢哪种变体/更常见?虽然 pure
方法更长,但对我来说 Control.Applicative typclass 看起来更笼统和真实。
这需要等效。事实上,在 Applicative
typeclass 的文档中,我们读到:
As a consequence of these laws, the
Functor
instance forf
will satisfyfmap f x = pure f <*> x
因为 (<$>) :: Functor f => (a -> b) -> f a -> f b
是:
An infix synonym for
fmap
.
因此认为:
f <$> x = pure f <*> x
因此可以使用两者来实现相同的目的。然而,由于 f <$> x
更短,而且可能更快(因为 (<*>)
需要处理所有 f a
),因此可能建议使用 (<$>)
。此外,作为 liftA2
的默认实现是 liftA2 f x = (<*>) (fmap f x)
,因此它也使用 fmap
(所以 <$>
)。
我想在这里简单地插一句,因为我有很多不受欢迎的观点,这就是其中之一,而你这么问 nyaaaaah。
承认:社区似乎或多或少同意 f <$> x <*> y <*> z
风格更好。但我其实更喜欢pure f <*> x <*> y <*> z
。我发现以应用风格编写的行往往比较长,因为每个参数本身通常是对函数的调用,所以:
fancyParser = FancyConstructor <$> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
-- OR
fancyParser = pure FancyConstructor <*> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
为了可读性,我经常将论点分成一行;我发现视觉上的分离让参数边界的位置更清晰,让我的眼睛稍微休息一下,并且减少了线条以丑陋的方式换行的可能性:
fancyParser = FancyConstructor
<$> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
-- OR
fancyParser = pure FancyConstructor
<*> fooParserWith 7 blag
<*> barParserSep "hi" (sizzleParser pop)
<*> bazParser
在这种形式中,我认为我更喜欢 pure
/<*>
的原因很清楚:它使行开头完全一致。一方面,这种一致性在视觉上很吸引人。但更重要的是,当我不可避免地重构 FancyConstructor
以重新排列字段的顺序,或在开头插入一个新字段时,我可以完全不受惩罚地使用逐行编辑命令。某些正则表达式搜索也变得稍微容易一些。它只能消除一点点摩擦……但是消除小摩擦是进行大型编程的一个重要部分。
P.S。我发现方便的其他不受欢迎的获得一致行格式的方法示例:
-- 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
]