こんにちは。病み(感染性胃腸炎)上がりの寺岡です。
暗黙の型変換(implicit conversions)といえば、Scala界隈では黒魔術的存在として有名ですが、
実はRubyにも暗黙の型変換が存在します。
Scalaの場合 任意の型 -> 任意の型 への変換が定義できますが、Rubyでは 任意の型 -> 特定の型 のみ定義することができます。
定義してみる
では実際にRubyの暗黙の型変換を定義してみましょう。
今回は配列への暗黙の型変換を定義してみます。
1 2 3 4 5 6 7 8 9 |
class PseudoArray def initialize(value) @value = value end def to_ary [@value] end end |
to_aryメソッドを定義して配列を返すだけです。
簡単ですね!
使ってみる
では使ってみましょう。
1 2 3 4 |
> pseudo_array = PseudoArray.new(5) > p [1,2,3,4] + pseudo_array [1, 2, 3, 4, 5] => nil |
見事、+演算子により配列との連結ができました。
では、+演算子のオペランドを逆にしてみたらどうなるでしょうか?
1 2 |
> p pseudo_array + [1,2,3,4] NoMethodError: undefined method `+' for #<PseudoArray:0x7fbf9da97b08 @value=5> |
Rubyでは演算子もメソッド呼び出しなので、PseudoArrayには定義されていない+メソッドを呼びだそうとしてエラーになります。
自前のメソッドでは...
Rubyでの暗黙の型変換は、特定の型が望まれている引数に与えた場合に効果を発揮します。
ただし、これは自動的に行われるわけではなく、関数の内部で引数の型検証と変換が行われています。
そのため、外部ライブラリや、自分で定義したメソッドで暗黙の型変換を行いたい場合は、以下のような変換処理を記述する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 引数に配列を必要とする何か def needs_array(array) array = case when array.kind_of?(Array) array when array.respond_to?(:to_ary) array.to_ary else raise TypeError, "can't convert #{array.class} into Array" end # do something... end |
対応している型
以下の型が暗黙の型変換に対応しています。
型 | 変換時に呼び出されるメソッド |
---|---|
Array | to_ary |
Hash | to_hash |
Integer | to_int |
IO | to_io |
Proc | to_proc |
Regexp | to_regexp |
String | to_str |
to_proc以外のメソッドについては、リファレンスマニュアルにメソッドを定義するための条件として、以下の様な記述があります。
・「対象の型」が使われるすべての場面で代置可能であるような、
・「対象の型」そのものとみなせるようなもの
どうやら、積極的に定義するべきではないようですね……。
暗黙の型変換の使い道
Rubyの暗黙の型変換は、自作のライブラリなどでは型変換処理を自前で行う必要があるため、どうしても利用シーンは限られてしまいます。
しかし、まったく使い道がないわけではありません。
ActiveSupportから取り込まれたSymbol#to_procなんかはこの機能を上手く使った例ですね。
Symbol#to_procをRubyで定義すると以下のような実装になります。
1 2 3 4 5 |
class Symbol def to_proc Proc.new{|obj| obj.send(self)} end end |
「引数に渡されたオブジェクトをレシーバとして、自身(シンボル)をメソッド名として実行した結果を返すProc」を返します。
メソッド呼び出し時の&つき呼び出しでto_procの結果をブロックとして与えることができるため、以下のような記述が可能になります。
1 2 |
> [1,2,3].map(&:to_s) # [1,2,3].map{|n| n.to_s} と同じ => ["1", "2", "3"] |
まとめ
定義する条件が厳しいことや、自作メソッドでは変換されないことから、暗黙の型変換を利用する機会は少ないかもしれません。
しかし、Symbol#to_procのように上手く使えば強力な機能になります。
自前のメソッドでは自動的な型変換は行われないので、
「ライブラリや再利用されるメソッドなどを書く時に、引数に暗黙の型変換処理をかけてあげる」
そんなさりげない優しさを持ってみてはいかがでしょうか。