撰写function组合:(。)。(。)如何工作?
 (.)需要两个函数,它们取一个值并返回一个值: 
 (.) :: (b -> c) -> (a -> b) -> a -> c 
 由于(.)有两个参数,所以我觉得(.).(.)应该是无效的,但它是完全正确的: 
 (.).(.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c 
这里发生了什么? 我意识到这个问题措辞严厉……所有的function都是由于咖喱而引起的。 也许更好的方式来说这是types不匹配。
让我们先玩typechecker的机械certificate。 之后我会描述一个直观的思考方式。
 我想将(.)应用于(.) ,然后将(.)应用于结果。 第一个应用程序可以帮助我们定义variables的一些等价性。 
 ((.) :: (b -> c) -> (a -> b) -> a -> c) ((.) :: (b' -> c') -> (a' -> b') -> a' -> c') ((.) :: (b'' -> c'') -> (a'' -> b'') -> a'' -> c'') let b = (b' -> c') c = (a' -> b') -> a' -> c' ((.) (.) :: (a -> b) -> a -> c) ((.) :: (b'' -> c'') -> (a'' -> b'') -> a'' -> c'') 
然后我们开始第二个,但很快就卡住了…
 let a = (b'' -> c'') 
 这是关键:我们想让let b = (a'' -> b'') -> a'' -> c'' ,但是我们已经定义了b ,所以我们必须尝试统一 – 以匹配我们最好的两个定义。 幸运的是,他们确实匹配 
 UNIFY b = (b' -> c') =:= (a'' -> b'') -> a'' -> c'' which implies b' = a'' -> b'' c' = a'' -> c'' 
通过这些定义/统一我们可以继续申请
 ((.) (.) (.) :: (b'' -> c'') -> (a' -> b') -> (a' -> c')) 
然后展开
 ((.) (.) (.) :: (b'' -> c'') -> (a' -> a'' -> b'') -> (a' -> a'' -> c'')) 
并清理它
 substitute b'' -> b c'' -> c a' -> a a'' -> a1 (.).(.) :: (b -> c) -> (a -> a1 -> b) -> (a -> a1 -> c) 
说实话,这是一个违反直觉的结果。
 这是直觉。 首先看看fmap 
 fmap :: (a -> b) -> (fa -> fb) 
 它将一个function“提升”成一个Functor 。 我们可以反复应用 
 fmap.fmap.fmap :: (Functor f, Functor g, Functor h) => (a -> b) -> (f (g (ha)) -> f (g (hb))) 
 使我们能够将function提升到更深层次的Functors 。 
 事实certificate,数据types(r ->)是一个Functor 。 
 instance Functor ((->) r) where fmap = (.) 
 这应该看起来很熟悉。 这意味着fmap.fmap转换为(.).(.) 。 因此, (.).(.)就是让我们转换(r ->) Functor的深层和深层参数types。  (r ->) Functor实际上是Reader Monad ,所以分层的Reader就像拥有多个独立的全局不变状态。 
 或者像有多个input参数不受fmap影响。  (> 1)参数函数的“只是结果”组成一个新的继续函数。 
最后值得注意的是,如果你觉得这个东西很有趣,它就构成了控制镜头的核心直觉。
让我们暂时忽略types,只使用lambda微积分。
- 
Desugar中缀符号: 
 (.) (.) (.)
- 
ETA-扩大: 
 (\ ab -> (.) ab) (\ cd -> (.) cd) (\ ef -> (.) ef)
- 
内联 (.)的定义:
 (\ abx -> a (bx)) (\ cdy -> c (dy)) (\ efz -> e (fz))
- 
替代 a:
 (\ bx -> (\ cdy -> c (dy)) (bx)) (\ efz -> e (fz))
- 
替补 b:
 (\ x -> (\ cdy -> c (dy)) ((\ efz -> e (fz)) x))
- 
替代 e:
 (\ x -> (\ cdy -> c (dy)) (\ fz -> x (fz)))
- 
替补 c:
 (\ x -> (\ dy -> (\ fz -> x (fz)) (dy)))
- 
替补 f:
 (\ x -> (\ dy -> (\ z -> x (dyz))))
- 
Resugar lambda表示法: 
 \ xdyz -> x (dyz)
 如果你问GHCi,你会发现这是预期的types。 为什么? 因为函数箭头是右关联的以支持柯里:types(b -> c) -> (a -> b) -> a -> c真的意味着(b -> c) -> ((a -> b) -> (a -> c)) 。 同时,typesvariablesb可以代表任何types, 包括函数types 。 看到连接? 
这是一个相同现象的简单例子:
 id :: a -> a id x = x 
id的types说id应该有一个参数。 事实上,我们可以用一个论据来说:
 > id "hello" "hello" 
但事实certificate我们也可以用两个参数来调用它:
 > id not True False 
甚至:
 > id id "hello" "hello" 
 到底是怎么回事? 理解id not True关键是先看看id not 。 显然,这是允许的,因为它将id应用于一个参数。  not的types是Bool -> Bool ,所以我们知道id的types应该是Bool -> Bool ,所以我们知道这个id的出现有type: 
 id :: (Bool -> Bool) -> (Bool -> Bool) 
或者,用更less的括号:
 id :: (Bool -> Bool) -> Bool -> Bool 
所以这个id的发生实际上有两个参数 。
 同样的推理也适用于id id "hello"和(.) . (.)  (.) . (.) 。 
这是我认为先把握一般情况,然后再考虑具体情况的简单方法之一。 所以我们来考虑仿函数。 我们知道仿函数提供了一种在结构上映射函数的方法 –
 class Functor f where fmap :: (a -> b) -> fa -> fb 
 但是如果我们有两层函数呢? 例如,列表的列表? 在这种情况下,我们可以使用两层fmap 
 >>> let xs = [[1,2,3], [4,5,6]] >>> fmap (fmap (+10)) xs [[11,12,13],[14,15,16]] 
 但是模式f (gx)与(f . g) x完全相同,所以我们可以写出来 
 >>> (fmap . fmap) (+10) xs [[11,12,13],[14,15,16]] 
 什么是fmap . fmap的typesfmap . fmap  fmap . fmap ? 
 >>> :t fmap.fmap :: (Functor g, Functor f) => (a -> b) -> f (ga) -> f (gb) 
 我们看到它映射了两层函数,就像我们想要的那样。 但现在请记住(->) r是一个函数(函数的types,你可能更喜欢读作(r ->) ),它的函子实例是 
 instance Functor ((->) r) where fmap fg = f . g 
 对于一个函数, fmap只是函数组合! 当我们fmap两个fmap我们映射了函数functor的两个级别。 我们最初有一些types(->) s ((->) ra) ,它相当于s -> r -> a ,而我们最终得到了typess -> r -> b ,所以types(.).(.)必须是 
 (.).(.) :: (a -> b) -> (s -> r -> a) -> (s -> r -> b) 
 它接受它的第一个函数,并使用它来转换第二个(双参数)函数的输出。 例如,函数((.).(.)) show (+)是两个参数的函数,首先将它们的参数加在一起,然后使用show将结果转换为String : 
 >>> ((.).(.)) show (+) 11 22 "33" 
 例如,有一个自然泛化思考更长的fmap链 
 fmap.fmap.fmap :: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (ha)) -> f (g (hb)) 
它映射三层函数,相当于由三个参数构成的函数:
 (.).(.).(.) :: (a -> b) -> (r -> s -> t -> a) -> (r -> s -> t -> b) 
例如
 >>> import Data.Map >>> ((.).(.).(.)) show insert 1 True empty "fromList [(1,True)]" 
 它将True值插入到键1的空映射中,然后使用show将输出转换为string。 
这些function通常是有用的,所以你有时看到它们被定义为
 (.:) :: (a -> b) -> (r -> s -> a) -> (r -> s -> b) (.:) = (.).(.) 
所以你可以写
 >>> let f = show .: (+) >>> f 10 20 "30" 
 当然,可以给出一个简单的,有针对性的(.:)定义 
 (.:) :: (a -> b) -> (r -> s -> a) -> (r -> s -> b) (f .: g) xy = f (gxy) 
 这可能有助于使(.).(.)神秘化。 
 你是对的, (.)只有两个参数。 你似乎混淆了haskell的语法。 在expression式(.).(.) ,它实际上是中间的点,将另外两个点作为参数,就像在expression式100 + 200中可以写成(+) 100 200 。 
 (.).(.) === (number the dots) (1.)2.(3.) === (rewrite using just syntax rules) (2.)(1.)(3.) === (unnumber and put spaces) (.) (.) (.) === 
 从(.) (.) (.)中应该更清楚地知道第一个(.)将第二个(.)和第三个(.)作为参数。 
 是的,这是由于咖喱。  (.)因为Haskell中的所有函数只有一个参数。 你正在创作的是第一次部分调用每个组合的(.) ,它接受它的第一个参数(组合的第一个函数)。 
(先阅读我的函数组合的答案 ,$运算符和无点风格。)
 想象一下,你有一个简单的function:它加起来2个数字,然后否定结果。 我们将它称为foo : 
 foo ab = negate (a + b) 
现在,让我们一步一步地将它看成是免费的,看看我们最终的结果:
 foo ab = negate $ a + b foo ab = negate $ (+) ab foo ab = negate $ (+) a $ b foo ab = negate . (+) a $ b foo a = negate . (+) a -- fx = gx is equivalent to f = g foo a = (.) negate ((+) a) -- any infix operator is just a function foo a = (negate.) ((+) a) -- (2+) is the same as ((+) 2) foo a = (negate.) $ (+) a foo a = (negate.) . (+) $ a foo = (negate.) . (+) foo = ((.) negate) . (+) foo = (.) ((.) negate) (+) -- move dot in the middle in prefix position foo = ((.) ((.) negate)) (+) -- add extra parentheses 
 现在我们来更仔细地分析expression式(.) ((.) negate) 。 这是(.)函数的部分应用,其第一个参数是((.) negate) 。 我们可以进一步改造它吗? 我们可以: 
 (.) ((.) negate) (.) . (.) $ negate -- because f (fx) is the same as (f . f) x (.)(.)(.) $ negate ((.)(.)(.)) negate 
  (.).(.)等价于(.)(.)(.) ,因为在第一个expression式中,中间的点可以移动到前缀位置,并用圆括号括起来,从而产生第二个expression式。 
 现在我们可以重写我们的foo函数: 
 foo = ((.).(.)) negate (+) foo = ((.)(.)(.)) negate (+) -- same as previous one foo = negate .: (+) where (.:) = (.).(.) 
 现在你知道(.).(.)等价于(\fgxy -> f (gxy)) : 
 (\fgxy -> f (gxy)) negate (+) 2 3 -- returns -5 ((.).(.)) negate (+) 2 3 -- returns -5