如何将$
和点自由样式结合起来使用?
下面是一个清晰的实用函数示例:
times :: Int -> [a] -> [a]
times n xs = concat $ replicate n xs
只是编写concat $ replicate
会产生错误,同样你也不能编写concat . replicate
,因为concat
期望的是一个值而不是一个函数。
那么你如何将上述函数转换为点·自由形式?
你可以使用这个组合符号:(冒号表示接下来跟着两个参数)
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)
它允许您去掉n
:
time = concat .: replicate
.:
更具有记忆性。 - fuz.*
,这样下一个可以是.**
, .***
等。无论哪种方式,我们都应该尝试将.:
引入Haskell Prime的Prelude中,或至少引入基本库中。 - Dan Burtonfmap fmap fmap
是 .:
的泛化吗? - nponeccop您可以轻松地使用以下方法编写几乎无参数版本:
times n = concat . replicate n
可以通过显式的柯里化和非柯里化实现完全无点的版本:
times = curry $ concat . uncurry replicate
加入 Freenode 并询问 lambdabot :)
<jleedev> @pl \n xs -> concat $ replicate n xs
<lambdabot> (join .) . replicate
(foo . ) . bar
是 lambdabot 的典型模式,用于 foo .: bar
,因为 .:
显然不被视为无意义的过程。 - Dan Burtonf . g . h == (f . g) . h == f . (g . h)
2 + 3 == (+) 2 3
f 2 3 = 2 `f` 3
组合运算符也是一个二元函数,它是高阶函数之一,接受两个函数并返回一个函数:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g == (.) f g
f . g . h == (f . g) . h == ((.) f g) . h == (.) ((.) f g) h
f . g . h == f . (g . h) == f . ((.) g h) == (.) f ((.) g h)
Haskell 中的每个函数默认都可以进行部分应用,这是由于其默认支持柯里化。中缀运算符可以使用段的方式非常简洁地进行部分应用:
(-) == (\x y -> x - y)
(2-) == (-) 2 == (\y -> 2 - y)
(-2) == flip (-) 2 == (\x -> (-) x 2) == (\x -> x - 2)
(2-) 3 == -1
(-2) 3 == 1
f . g == (.) f g == (f.) g == (.g) f
另一个有趣的二元运算符是$,它只是函数应用:
f x == f $ x
f x y z == (((f x) y) z) == f x y z
f(g(h x)) == f $ g $ h $ x == f . g . h $ x == (f . g . h) x
concat $ replicate n xs
转换为点式风格?times n xs = concat $ replicate n xs
times n xs = concat $ (replicate n) xs
times n xs = concat $ replicate n $ xs
times n xs = concat . replicate n $ xs
times n = concat . replicate n
times n = (.) concat (replicate n)
times n = (concat.) (replicate n) -- concat is 1st arg to (.)
times n = (concat.) $ replicate n
times n = (concat.) . replicate $ n
times = (concat.) . replicate
¹Haskell基于范畴论。在范畴论中,一个范畴包括三个要素:一些对象、一些态射以及关于态射的复合的概念。每个态射连接一个源对象和一个目标对象,是单向的。范畴论要求态射的复合是结合的。在Haskell中使用的范畴称为Hask,其对象是类型,而其态射是函数。一个函数f :: Int -> String
是将对象Int
连接到对象String
的一个态射。因此,范畴论要求Haskell中的函数复合是结合的。
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)
(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.::) = (.).(.:)
(.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.:::) = (.).(.::)
...
额外奖励
(.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.:::) = (.:).(.:)
嗯...所以也许我们应该这样说
(.1) = .
(.2) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.2) = (.1).(.1)
(.3) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.3) = (.1).(.2)
-- alternatively, (.3) = (.2).(.1)
(.4) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.4) = (.1).(.3)
-- alternative 1 -- (.4) = (.2).(.2)
-- alternative 2 -- (.4) = (.3).(.1)
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f = fmap (fmap f)
fmap4 :: (Functor f, Functor g, Functor h, functro i)
=> (a -> b) -> f (g (h (i a))) -> f (g (h (i b)))
fmap4 f = fmap2 (fmap2 f)
遵循相同模式。
最好能够将应用fmap
或(.)
的时间参数化。然而,这些fmap
或(.)
实际上在类型上是不同的。因此,唯一的方法是使用编译时计算,例如TemplateHaskell
。
对于日常使用,我只会简单建议
Prelude> ((.).(.)) concat replicate 5 [1,2]
[1,2,1,2,1,2,1,2,1,2]
Prelude> ((.).(.).(.)) (*10) foldr (+) 3 [2,1]
60