考虑以下功能:
foo =
[1,2,3] >>=
return . (*2) . (+1)
为了更好的可读性和逻辑性,我想将纯函数(*2)
和(+1)
移到返回的左侧。我可以这样做:
infixr 9 <.
(<.) :: (a -> b) -> (b -> c) -> (a -> c)
(<.) f g = g . f
bar =
[1,2,3] >>=
(+1) <.
(*2) <.
return
但是,我不喜欢(<.)
的正确联想。
让我们介绍一个函数leftLift
:
leftLift :: Monad m => (a -> b) -> a -> m b
leftLift f = return . f
baz =
[1,2,3] >>=
leftLift (+1) >>=
leftLift (*2) >>=
return
我非常喜欢这个。另一种可能性是定义bind
的变体:
infixl 1 >>$
(>>$) :: Monad m => m a -> (a -> b) -> m b
(>>$) m f = m >>= return . f
qux =
[1,2,3] >>$
(+1) >>$
(*2) >>=
return
我不确定这是否是一个好主意,因为如果我想要的话,它不允许我使用do
符号。 leftLift
我可以使用do
:
bazDo = do
x <- [1,2,3]
y <- leftLift (+1) x
z <- leftLift (*2) y
return z
我在Hoogle上找不到具有leftLift
签名的功能。这样的功能是否存在,如果,它被称为什么?如果没有,我该怎么称呼它?做我想做的事最惯用的方式是什么?
编辑:这是一个受@ dunlop答案的启发版本:
infixl 4 <&>
(<&>) :: Functor f => f a -> (a -> b) -> f b
(<&>) = flip fmap
blah =
[1,2,3] <&>
(+1) <&>
(*2) >>=
return
我还应该补充一点,我追求bind
- 变种,因为我想用无点样式编写代码。对于do
- 符号,我想我不需要&#34;假装&#34;我做任何monadic,所以我可以使用let
s。
答案 0 :(得分:4)
每个Monad
都是Functor
(也是Applicative
)。您的(>>$)
是(翻转)fmap
。
GHCi> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
GHCi> :t (<$>) -- Infix synonym for 'fmap'
(<$>) -- Infix synonym for 'fmap'
:: Functor f => (a -> b) -> f a -> f b
GHCi> fmap ((*2) . (+1)) [1,2,3]
[4,6,8]
GHCi> (*2) . (+1) <$> ([1,2,3] >>= \x -> [1..x])
[4,4,6,4,6,8]
(顺便说一句,翻转fmap
的常用名称是(<&>)
。例如,镜头调用它。)
如果您使用的是do-notation,则几乎没有理由明确使用fmap
的任何变体进行此类转换。只需为let-bindings切换<-
monadic绑定:
bazDo = do
x <- [1,2,3]
let y = (+1) x
z = (*2) y
return z
bazDo = do
x <- [1,2,3]
let y = (+1) x
return ((*2) z)
答案 1 :(得分:1)
为了更好的可读性......
这将是主观的,因为人们不同意什么是可读的。
话虽这么说,但我同意,当从左到右书写时,有时会更容易理解数据转换。不过,我认为你的>>$
有点矫枉过正了。 &
中的Data.Function
运算符完成了这项工作:
import Data.Function
foo = [1,2,3] & fmap (+1) & fmap (*2)
我喜欢这说明了从左到右的每个步骤的确切内容以及确切的操作。与>>$
不同,你不会被迫留在monad中:
bar = [1,2,3] & fmap (+1) & fmap (*2) & sum & negate
或者您可以事先组装转换并将其映射到您的monad:
import Control.Category
f = (+1) >>> (*2)
quuz = fmap f [1,2,3]