NOTE: ここでは以下のように定義します。
- functional object
#call
(と#<<
#>>
) が定義されているオブジェクト- e.g.
Proc
Method
- blockable object
#to_proc
が定義されているオブジェクト- e.g.
Symbol
Hash
Proc#<<
やProc#>>
は functional オブジェクトを受け取るProc#<<
やProc#>>
は内部で#to_proc
を呼び出さないProc#<<
やProc#>>
はブロック引数は受け取らない
- functiona オブジェクト と functiona オブジェクトを組み合わせた式から成り立つ
functional object >> functional object
# => OKfunctional object >> other object
# => NGother object >> functional object
# => NG
-
Symbol
は blockable オブジェクト -
Symbol
は functional オブジェクトではない -
関数合成で blockable オブジェクトを扱うのはおかしい
Proc << Symbol
やSymbol << Proc
-
他の
#to_proc
が定義されているオブジェクトはどう扱うべきかHash
とかHash
も functional オブジェクト?
#to_proc
で明示的にProc
(functional オブジェクト) に変換するべきproc << :hoge
=> NG: :hoge はProc
ではないproc << :hoge.to_proc
=> OK ::hoge
を明示的にProc
に変換
- これは
"42"
が数値として扱われないのに似ている1 + "42"
→ NG :"42"
は数値ではない1 + "42".to_i
→ OK :"42"
を明示的に数値に変換している
Symbol#>>
Symbol#<<
Symbol#call
を定義するSymbol
以外の blockable オブジェクトはどうするべき?- この機能は本当に
Symbol
に必要?
class Symbol
def call(*args, &block)
to_proc.call(*args, &block)
end
def <<(other)
to_proc << other
end
def >>(other)
to_proc >> other
end
end
p %w{72 101 108 108 111}.map(&(:to_i >> :chr))
# => ["H", "e", "l", "l", "o"]
Proc#<<(other)
をProc#<<(other, &block)
にするother
を優先する
- ブロック引数を渡す場合は
proc.<< &:hoge
と書く必要がある
class Proc
prepend Module.new {
def <<(other = nil, &block)
# other or block?
super(other || block)
end
def >>(other = nil, &block)
# other or block?
super(other || block)
end
}
end
# :to_i convert to Proc
# must be `.>>`
p %w{72 101 108 108 111}.map(&(:to_i.to_proc.>> &:chr))
# => ["H", "e", "l", "l", "o"]
- 例えば
~@
を#to_proc
のエイリアスにしてしまう - 現行の仕様を変更する事なく
Symbol
などの#to_proc
が定義されているオブジェクトで関数合成できる - 現状だとこれがいいと思う
class Object
# ~ is to_proc
# ~ or other unary operator?
def ~@
to_proc
end
end
# Use Symbol#to_proc
p %w{72 101 108 108 111}.map(&(:to_i.to_proc >> :chr.to_proc))
# alias ~ is to_proc
p %w{72 101 108 108 111}.map(&~:to_i >> ~:chr)