擬似中置記法
OCamlでは、modは特別扱いで中置記法として定義されています。
# (mod);; - : int -> int -> int = # 5 mod 3;; - : int = 2 |
たとえばこの別名でremという関数を作りたくても、これを中置記法で定義することは出来ません。
# let (rem) x y = x mod y;; Error: Syntax error |
これは、中置演算子として定義できる文字列が、記号の特定の組み合わせ、もしくは予約された文字列、というふうに決められているからですね。
中置演算子として予約された文字列にはor,mod,land,lor,lxor,lsl,lsr,asrがあります。
(参考:公式マニュアルのinfix-opの項)
ここで、パイプ演算子を使うと、2引数関数を擬似的に中置記法で書くことができます。
# let (|>) x f = f x;; val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun> # let (<|) f x = f x;; val ( <| ) : ('a -> 'b) -> 'a -> 'b = <fun> # let rem = (mod);; val rem : int -> int -> int = <fun> # 5 |>rem<| 3;; - : int = 2 |
中置演算にしたい関数を、|>と<|で挟み込むわけですね。パイプ演算子として読めば、データを両方から流しこむことになりますし、ひとまとめの記号として読めば中置演算子になります。ちなみに、Haskellerの友人はパイプ演算子がちょうど逆の記法になって、<|rem|>みたいな書き方をしていました。
このほうが、ひとまとめの中置演算記号っぽさは出ますね。
この場合はパイプ演算子ではなく、関数適用というイメージでしょうか。
ちなみに、演算子として定義可能な名前operator-nameは以下のように定義されています。
(参考:公式マニュアル)
operator-name ::= prefix-symbol | infix-op infix-op ::= infix-symbol | * | = | or | & | := infix-symbol ::= ( = | < | > | @ | ^ | | | & | + | - | * | / | $ | % ) { operator-char } prefix-symbol ::= ( ! | ? | ~ ) { operator-char } operator-char ::= ! | $ | % | & | * | + | - | . | / | | < | = | > | ? | @ | ^ | | | ~ |
既に定義されている演算子も上書き可能であったり、変数として利用することもできるので、以下のような定義も可能です。
# let (+) = (-) and (-) = (+);; val ( + ) : int -> int -> int = <fun> val ( - ) : int -> int -> int = <fun> # let (<) = (>) and (>) = (<);; val ( < ) : 'a -> 'a -> bool = <fun> val ( > ) : 'a -> 'a -> bool = <fun> # let (!) = 1;; val ( ! ) : int = 1 # let (!?) = 2;; val ( !? ) : int = 2 # (!) + (!?);; - : int = -1 # (!) - (!?);; - : int = 3 # (!) + (!?) < (!?);; - : bool = false # (!) - (!?) > (!);; - : bool = false |
……わけがわかりませんね。
使い所は難しいですが、まぁこんなことも出来るよ、ということで。