Skip to content

Instantly share code, notes, and snippets.

@torao
Last active January 18, 2018 05:43
Show Gist options
  • Save torao/d6b762eb6f6b117e89cdfba27b232f66 to your computer and use it in GitHub Desktop.
Save torao/d6b762eb6f6b117e89cdfba27b232f66 to your computer and use it in GitHub Desktop.

多重ディスパッチはオブジェクト指向で言うところの多態化となるか?

Julia の多重ディスパッチは OO で言うところの多態化となるか? についてのポエム。

target version: Julia 0.6.2

普段は Scala, Java, Python, Ruby あたりを使っていますが、Julia はまだ言語仕様を見ながらコードを書いている状況なので、Julia を使いこなせる方が「えっそれをするためにこういう機能があるよ?」(知ってるかどうか問題だったオチ) であれば話は終わります。多分、それ以上読み進めるのは時間の無駄です。


Julia の多重ディスパッチは実行時の型から対応する関数 (振る舞い) を推定して選択するが、関数を使用する時点で全ての可能性のある型について自明でなければならない制約があります。

例えば以下のソースでは save() に渡される可能性のある型 A, B に対して、それぞれ JSON 形式の文字列を参照する関数 to_json(::A), to_json(::B) が  save() 時点で決定しているため多態のように振る舞うことができます。

module Sample1

  # A と B の型があり
  struct A x::Int end
  struct B y::Int end

  # それぞれにディスパッチ可能な関数が自明なら
  to_json(a::A) = "{x:$(a.x)}"
  to_json(b::B) = "{y:$(b.y)}"

  # 多重ディスパッチで obj の型から対応する関数を選択/実行することができる
  save(obj) = println(to_json(obj))

  save(A(2018))  # {x:2018} → OK
  save(B(0116))  # {y:116} → OK
end

これは to_json() の挙動が型に合わせて適切に置き換わっているように見えます。では開発担当を複数人に分けてモジュールももう少し複雑になったときも同じ方法でできるでしょうか。

他人が作ったフレームワークや共通機能を使う場合、save() の実装時点で obj として渡される可能性のある (どこかの馬の骨が作ったか分からない) 型 B および対応する to_json(::B) は参照できません。

module Sample2
  module Framework
    export A, save
    # 型 A のみ対応する関数が定義されている
    struct A x::Int end
    to_json(a::A) = "{x:$(a.x)}"

    # 実行時に obj の型から解決できるのは A だけ
    save(obj) = println(to_json(obj))
  end
  module Application
    using Sample2.Framework

    # 型 B (本来は A のサブタイプにしたいところだが) に対応する関数は当然ながら Framework.save()
    # からは不可視
    struct B y::Int end
    to_json(b::B) = "{y:$(b.y)}"

    save(A(2018))  # {x:2018} → OK
    save(B(0116))  # ERROR: MethodError: no method matching to_json(::Sample2.Application.B)
  end
end

この場合 save(obj, to_json::Function) のように定義して obj に対応する振る舞いの関数 to_json(::B) をセットで渡さなければならず、多重ディスパッチは多態化を代替する機能にはなっていません。

一方で OO の場合は to_json(::B) 関数は obj::B に付随するため save() 内で obj の具体型が何かを認識する必要はなく、save() は (A の サブタイプであれば) 任意の型を取ることができます。つまり save() に渡される可能性のある型が実装時点で全て自明である必要がない点が違います。OO でライブラリ設計を行う開発者はこの動作を前提としています。

擬似コードで表すなら以下のような感じ。

module Framework
  export A, save
  class A
    x::Int
    to_json() = "{x:$(this.x)}"
  end
  save(a::A) = println(a.to_json())
end
module Application
  using Framework
  class B(x) <: A(x)
    y::Int
    to_json() = "{x:$(super.x),y:$(this.y)}"
  end
  save(A(2018))        # {x:2018}
  save(B(2018, 0116))  # {x:2018,y:116} ← 期待する動作
end

「オブジェクトごと B.to_json() を渡すことと to_json(::B) 関数で別に渡すことは実質同じだ」という意見はごもっともです。ただ、こういった問題を簡潔かつ安全に解決できるよう設計された言語かという点が OO のパラダイムを持つ言語かの判断になります。でないと関数ポインタがあるんだから C 言語は実質多態化が可能だ (第一級関数を持つ言語は全て多態化が可能だ) ということになり OO is 何? となってしまうので「○○すれば××できる」論議は不毛です。

以上より、私としては「多重ディスパッチは特定の状況下で多態化の代替として機能するが、多態化そのものの代替となりうる機能ではない」と考えています。

いまのところ Julia は計算科学方面のペラッとしたコードを書く用途が多いようなので多重ディスパッチでも十分機能すると思いますが、今後汎用言語として Python や Ruby の代替を目指すなら、複雑にモジュール化した実装を行う上で露呈してくる部分かなと思っています。

なお Pyhon や Ruby のように将来の Julia のバージョンでクラスやインターフェース等の OO 的なパラダイムが追加されても問題のない言語仕様になっているとは思います (開発側にその意思があるかは分かりませんが)。

@torao
Copy link
Author

torao commented Jan 17, 2018

つたない知識に詳しくご説明いただきありがとうございました。
今まで使い慣れた設計パターンを考え方から再構築が必要で辛いところですが、まぁ何とかなるでしょう。

@torao
Copy link
Author

torao commented Jan 17, 2018

最後に、発端に戻って誤解のないように書いておきますが、私は言語それぞれの背景にある理念や文化を知ることを楽しんでいますので、他の言語のパラダイムを持ち込んで「Julia は OO がないからダメだ!」という考えはありません (むしろそういった原理主義とは対極のポジションです)。

Julia に限らず新しい言語やシステム等に着手するときには:

  1. 今までの考え方がどこまで通用するのか (何があって何が無いのか)?
  2. それに浸かるために新しく導入しなければならない考えは何か?
  3. それに浸かるために今の考えから捨てなければならないことは何か?

を手探りしています。そしてその過程から出る「Julia は OO がないな、手続き型で書くのは辛いな」というような何かを試行錯誤をしているときの一連のツイートは:

  • 現時点の疑問点/これから調べようとしていることについて、何が分かっていない/出来ていないか言語化してもやっと感をはっきりする
  • 作業を中断するために現時点で何が分かっていない/出来ていないかを出力しておく
  • 現時点の理解で「それが何であるか」を自分なりの表現で言語化して確認する

といったことを目的とした可視化作業です (Twitter はそれを気軽にできる最適なツールです; まぁ誰かに「それ○○でできるよ」とキーワードを頂けるかもなスケベ心も否定はしませんが)。

ただ、それらを逐一検索で拾って「こいつは何も分かっていない! ロクに調べもせずに Julia を非難している!」と何らかの悪意を見いだされているような方がいらっしゃいますが、それは考えすぎです (正確に言うときっかけとなった引用ツイートにそういった意図を感じたためこの記事といくつかのツイートに関してはいささかうんざりしながら(+深夜遅くもあり)後ろ向きな気分で書いています)。

@bicycle1885
Copy link

私も別に論破してやろうとか,そういう気持ちがあってコメントしているわけではないです。新しい言語を習得する際には先入観はつきものです。ただ,もしJuliaに対して誤解があったら残念なので,もし何か思い違いがあるのではないかと思った記事や意見に対してはコメントしようと思っています。こうして具体的にコードを示して文章化して貰えると,議論しやすくなるので大変助かります。

Juliaは新しい言語ですが,既存の言語をよく研究して設計されているので,多くの問題には標準的な解決法が用意されていると思います。しかしこれらが文章化されていることは少ないので,既存のよくできたソースコードを参考にするか,フォーラム(discourseなど)で質問するのが良いと思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment