Skip to content

Instantly share code, notes, and snippets.

@osyo-manga
Created January 8, 2019 14:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save osyo-manga/1725a4a670aac54452eca92269a3822b to your computer and use it in GitHub Desktop.
Save osyo-manga/1725a4a670aac54452eca92269a3822b to your computer and use it in GitHub Desktop.

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#>> はブロック引数は受け取らない

Ruby の関数合成

  • functiona オブジェクト と functiona オブジェクトを組み合わせた式から成り立つ
  • functional object >> functional object # => OK
  • functional object >> other object # => NG
  • other object >> functional object # => NG

Symbol は functional オブジェクト?

  • Symbol は blockable オブジェクト

  • Symbol は functional オブジェクトではない

  • 関数合成で blockable オブジェクトを扱うのはおかしい

    • Proc << SymbolSymbol << Proc
  • 他の #to_proc が定義されているオブジェクトはどう扱うべきか

    • Hash とか
    • Hash も functional オブジェクト?

Proc#<< などで #to_proc を呼び出すべき?

  • #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" を明示的に数値に変換している

案1 : Symbol を functional オブジェクトとして扱う

  • 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"]

案2 : Proc#<< などでブロック引数を受け取る

  • 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"]

案3 : #to_proc のシンタックスシュガーを定義する

  • 例えば ~@#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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment