Scalazは, 以下のような演算子をApplyで定義している: (m1 |@| m2 |@| m3) f. このような, ちょっとかっこいい演算子はsyntax/というディレクトリ以下に実装されている.
これはマコーレー・カルキン演算子と呼ばれる. 実装が面白いので紹介する.
マコーレーカルキン演算子でN個の値をつなぐと, applyNのことであると書いてある. 例えば, f1 |@| f2 |@| f3は, 次に関数を受けて, apply3相当のことをする.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* DSL for constructing Applicative expressions.
*
* `(f1 |@| f2 |@| ... |@| fn)((v1, v2, ... vn) => ...)` is an alternative to `Apply[F].applyN(f1, f2, ..., fn)((v1, v2, ... vn) => ...)`
*
* `(f1 |@| f2 |@| ... |@| fn).tupled` is an alternative to `Apply[F].applyN(f1, f2, ..., fn)(TupleN.apply _)`
*
* Warning: each call to `|@|` leads to an allocation of wrapper object. For performance sensitive code, consider using
* [[scalaz.Apply]]`#applyN` directly.
*/finaldef|@|[B](fb:F[B])=newApplicativeBuilder[F, A, B]{vala:F[A]=selfvalb:F[B]=fb}
finalclassApplicativeBuilder[M[_], A, B](a:M[A],b:M[B]){defapply[C](f:(A,B)=>C)(implicitt:Functor[M],ap:Apply[M]):M[C]=ap(t.fmap(a,f.curried),b)finalclassApplicativeBuilder3[C](c:M[C]){defapply[D](f:(A,B,C)=>D)(implicitt:Functor[M],ap:Apply[M]):M[D]=ap(ap(t.fmap(a,f.curried),b),c)
ap(t.fmap(a. f.curried), b)というのは, Haskellでいうと, f <$> a <*> bのことである. なので, apply2とかを使う方が綺麗でいいと思うのだが, なぜそうなっていないのかは不明. Scalaで関数型プログラミングをしようとすると, このcurriedというのがかなりばら撒かれるか, あるいはcurriedしなくても済むように, よくあるケースに対してライブラリが努力するという方向になる(Scalazはたぶんそうしている). 言語の方で自動的にcurried化するようにがんばって欲しいのだが, 無理なんだろうか?