#Julia言語 の多重ディスパッチを強く支持している人達は、「多重ディスパッチのJuliaにはクラスベースのOOPの機能を追加するべきではない」「OOPのデザインパターンはOOPの欠陥を補完するものであり、多重ディスパッチではよりシンプルな方法で解決できる場合が多い」と主張しています。続く
#Julia言語 多重ディスパッチとは、x,y,zの型の組み合わせを変えたとき同名の函数f(x,y,z)で異なるメソッドを実行可能にする機能のことです。x,y,zは平等に扱われる。

f(x,y,z)におけるxおよびその型のみを特別扱いしてx.f(y,z)と書けるようにすれば、よく見るOOPのスタイルになる。

続く
#Julia言語 資料

C++の開発で知られるBjarne Stroustrupさんは、f(x,y,z)をx.f(x,y)と書くスタイルの採用は浅い考えに基く失敗で、多重メソッド(=多重ディスパッチ)の採用が良さそう、のように言っているように見えます。

discourse.julialang.org/t/is-julias-wa…

open-std.org/jtc1/sc22/wg21…
#Julia言語 資料

多重ディスパッチは1980年代にCommon Lispで採用されており、Juliaはパラメータ付きの型による多重ディスパッチを使って全体が設計されています。

歴史的にプログラミング言語界で多重ディスパッチは余り広まらなかったのは不思議だという話↓

discourse.julialang.org/t/is-julias-wa…
#Julia言語 資料:多重ディスパッチの重要な応用例

①線形代数の効率的な実装
②数値型のプロモーション問題の解決
③オブジェクトの文脈依存の表示

discourse.julialang.org/t/is-julias-wa…

②にSwiftは苦労↓
github.com/apple/swift-ev…
【This proposal~DOES NOT solve the integer promotion problem】
#Julia言語 数値型のプロモーション(昇格)の問題とは、例えば a+bにおいて、aがBigInt型でbがFloat64型のとき、aとbの型をBigIntとFloat64の共通の昇格先として使えるBigFloatに変換してから足し算を計算するようにせよ、というような話です。
#Julia言語 多重ディスパッチでは、a+bにおけるa,bの型の組み合わせの違いでa+bで実際に実行されるメソッドを違うものにできるので、数値型の昇格の問題は自然に解決可能です。
#Julia言語 線形代数では、様々な特殊な形の行列が重要な役目を果たします。対角行列、上三角行列、三重対角行列、対称行列、…。

それらに通常の共通の演算記号を使用できてかつ、行列の型の組み合わせごとに効率的なメソッドを実装することは、多重ディスパッチでは容易です。
#Julia言語 f(a,b,c,d,…)におけるa,b,c,d,…の型の組み合わせによって、同名の函数fで実行されるメソッドが別のものになるようにできる、という仕様はシンプルで自然に見えます。

他のプログラミング言語でも広く採用されていて良い仕様だと思うのですが、現実にはそうなっていない。非常に不思議。
#Julia言語 資料:多重ディスパッチの重要な応用例の追加

④拡張が容易なplot函数

matplotlibとの比較↓

discourse.julialang.org/t/is-julias-wa…
#Julia言語 関連スレッド

多重ディスパッチでは

①既存の型達の組み合わせへの新しいメソッドの追加

②既存の函数への新しいメソッドの追加

の両方が易しいという話↓
繰り返しになりますが、

f(a,b,c,d,…)におけるa,b,c,d,…の型の組み合わせによって、同名の函数fで実行されるメソッドが別のものになるようにできる

という仕様が「普通」になっていないことが非常に不思議。
#Julia言語 資料

多重ディスパッチなプログラミング言語間での引っ越し(Common Lisp → Julia)については以下を参照。

Juliaで採用されたパラメータ付きの型によるディスパッチが強力という話↓

tamaspapp.eu/post/common-li…
Switching from Common Lisp to Julia
2017/10/15
#Julia言語 資料

「Juliaは本質的にFortranモデルを実装している」とおかしなことを言う人に対する対応。

でも、多重ディスパッチのお陰でクラスが無用であることが明瞭になっているJuliaのスタイルを「単なる函数群のべた書き」のように誤解するパターンは結構見る。

discourse.julialang.org/t/about-inheri…
#Julia言語 C++の開発者はx.f(y,z)のスタイルは間違いだったと言っていますが、Juliaでもf(x,y,z)をx.f(y,z)と書けるようにできます(笑) 冗談の1つとして遊びで行われている。

私もやった→ gist.github.com/genkuroki/3d29…
#Julia言語 関連

discourse.julialang.org/t/about-inheri…
【There is also github.com/mauro3/OO.jl 😉
(please don’t use it!)】

「使わないでね😉」的な冗談パッケージです。

Juliaではメソッドはクラスの保有物だとは考えないのですが、そのように見えるような実装は可能です。
#Julia言語 私によるDistributions.jlの"x.f(y,z)"化の解説

dist1 = 平均0標準偏差1の正規分布オブジェクト
dist2 = 平均0標準偏差1.001の正規分布オブジェクト

dist1が「保有」するisapproxメソッドでdist2と比較している(ように見える)。
相対誤差0.01でdist1とdist2は近似的に等しい。
#Julia言語 Juliaでは普通

dist1 ≈ dist2

とか

isapprox(dist1, dist2; rtol=1e-2)

と書きます。函数 a ≈ b, isapprox(a, b) はa,bの型ごとに違うメソッドを実行するようになっています。

Distribution型のオブジェクトxについて、f(x,y,z)をx.f(y, z)とも書けるようにする方法↓(容易)
#Julia言語 多重ディスパッチを理解して、"x.f(y,x)"型の書き方もできるようにする遊びをやってみると分かることは、"x.f(y,x)"型の書き方にこだわる必然性は皆無だということ。

C++の開発者もx.f(y,z)スタイルの採用は間違いだったと言っている。
#Julia言語 Julia言語の根幹部分に "x.f(y,z)" スタイルを導入することはナンセンスだと思うのですが、PyCall.jlのようなJuliaから他のOOP言語を使えるようにするパッケージではJuliaでも "x.f(y,z)" スタイルも可能なことが便利に使われています。

その辺についてJuliaコミュニティは柔軟です。
#Julia言語 ただし、わざわざJulia好きの人が集まっている場所で、「JuliaはFortranモデルを実装している。OOPをJuliaに導入せよ」とバカ丸出しで喧嘩を売って来たり、OOPでの「デザインパターン」が至上のものであるかのような態度を取る人がいて「面白いこと」になる場合がある。
#Julia言語 個人的な意見では、"x.f(y,z)" スタイルの最大の利点は「タブ補完の実装が容易なこと」です。

「x. タブ」でオブジェクトxに実装されているメソッド一覧が表示されるのはマジ便利。

私の「実装」でもBase. propertynamesの設定によって「dist. タブ」で補完できるようにしています(笑)
#Julia言語 多重ディスパッチでは、メソッドが1つのオブジェクトに保有されていると考えることはできず、複数のオブジェクトの型の組み合わせで使用できるメソッドが決まります。タブ補完の仕様の決定自体が難しい問題になる。しかし以下を参照↓

github.com/JuliaLang/juli…
#Julia言語 あと、わざわざJulia好きが集まる場所にやって来て、「Juliaでも継承を実装しろ」と言うパターンも検索すると結構出て来る。

よくある回答は「compositionを使えないのか?」「minimal working exampleを示して下さい」です。

結果的にJuliaと無関係な話題になる。
#Julia言語 しかし、少々無茶なことを言っていても、具体的なコードで実際に動くもの(minimal working example)をしっかり示して、「どうすればいいのか?」と言っている場合の議論は初心者ユーザーの私にとってありがたいことが多いです。

「論よりコード」だと思う。
#Julia言語 確率分布を扱うためのパッケージDistributions.jlについて、"x.f(y,z)"化を試してみた理由は、そのパッケージが例外的に "x.f(y,z)" スタイルと相性が良いからです。

確率分布を1つのオブジェクト(例えば Normal(2, 3))とみなすこと自体はそれなりに自然な考え方です。続く
#Julia言語 平均2標準偏差3の正規分布オブジェクトが擬似乱数を発生させるメソッドや確率密度函数のメソッドを保有していると考えることは、個人的に十分に自然な考え方だと思います。

実際にDistributions.jlはそのようにデザインされています。(ただしJuliaなので"x.f(y,z)"スタイルではない)続く
#Julia言語 しかし、確率分布はそのパラメータの対数尤度函数として使われることが非常に多い。例えば正規分布のデータdataに関する対数尤度函数は

L(μ, σ, data) = sum(logpdf(Normal(μ,σ), x) for x in data)

と書けます。確率分布オブジェクトのコンストラクタNormal(μ,σ)を経由して定義される。
#Julia言語 続き。だから、確率分布をオブジェクトとみなす場合には、対数尤度函数が効率的に計算可能になるためには、確率分布オブジェクトのコンストラクタで遅延が発生しないように頑張る必要が生じます。

実際にDistributions.jlはそこで頑張っています。続く
#Julia言語

そういう頑張りがなくても効率的に計算できるようにもできるのですが、使い易さとのバランスが悪くなる可能性がある。

確率分布を効率的に扱う方法にはさらなる工夫があり得ると思います。

誰か、研究して、Distributions.jlよりも効率的なパッケージを実験的に書くと面白いかも。
#Julia言語 実際、対数尤度函数の実装はDistributions.jlを経由せずに、直接べた書きした方が速い場合が結構あります。この辺にも研究と改善の余地が残っている。
パラメータ付き確率分布 p(x|θ) の実装では、外部からxやθが動ける範囲が見えている必要があります。

Distributions.jlでは dist(θ) をオブジェクト化しているのですが、θ抜きでオブジェクト化するという考え方もあります。
#Julia言語

discourse.julialang.org/t/julia-motiva…
要約【多重ディスパッチとの出会いは20年前の大学でのCommon Lispを使った授業でのCLOS。C ++の方法よりもずっと綺麗で強力だと思った。約10年前にClosure学んだときや、数年後にRのS4システムについて学んだときにCLOSでの経験が活かされた。】
#Julia言語 大昔に大学の授業でCommon LispでCLOSについて学んだ人達が多重ディスパッチの歴史を紡いで来たということなんですかね。
#Julia言語 多重ディスパッチ関連の話題から脱線。

「どうして Python + Cython, Numba, Pythran よりも Julia が優れていると考えられるか」についてのJulia側の考え方は、以下のリンク先スレッドのリンク先以降を見れば分かります。

discourse.julialang.org/t/julia-motiva…
#Julia言語 Juliaはよく「速い」と宣伝されているのですが、Juliaコミュニティでの議論を見ると、「単に速いだけでは足りない!」という主張がしつこく繰り返されています。
#Julia言語 例えば、ある特定のコンテナ(例えばNumPy配列)のみについて速いコードを書いて満足してはいけないというのが、Julia側の基本的主張。

多くの場合に高速なアルゴリズムを記述した同一のコードを、特定のハードウェアに特化されていたりされていなかったりする様々なコンテナに適用したい。
#Julia言語 これは、

①既存の型への新メソッドの追加
②既存の函数の新しい型への適用

の②にあたることです。

自分で書いた函数を通常の配列の型に適用して使っているとき、それを高速化するためにハードウェアを活かした別の配列の型にも適用するというようなことができればうれしい。
#Julia言語 さらに、複数のライブラリやパッケージを組み合わせて使いたい。

そのときもしも異なる正弦函数foo.sinとbar.sinがあって、fooにおける機能はfoo.sinにしか使えず、barにおける機能はbar.sinにしか使えないようになっていると、fooとbarをうまく組み合わせて使えなくなる。
#Julia言語 以上で述べた問題を解決したり、適切な対処法を用意するためにJulia開発者達が選んだのが、多重ディスパッチだったわけです。
#Julia言語

AbstractArray型で動くコードは、AbstractArrayのsubtypeになっているハードウェアに特化した機能を持つ配列でも使用できる。

Juliaでは foo.sin とか bar.sin と書いたりしないので、sin が定義されている型には同名の sin がいつでも適用可能。

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with 黒木玄 Gen Kuroki

黒木玄 Gen Kuroki Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @genkuroki

28 Mar
「立式」は「問題を解くために役に立つ式を作ること」という意味だと誤解する人が多いので、たとえ鉤括弧付きでも使用する場合には説明を付けた方がよいと思いました。

「立式」の意味は概ね「『式  答え 』形式の解答欄の式の項目に先生が暗黙のうちに要求している式を書くこと」です。続く
そして、暗黙のうちに先生が前提にしていることは、

* 場面や考え方を忠実に表現する式が決まっている。
* 問題文をそういう式に変換する「立式」が重要である。

です。これは極めて有害な考え方なので、「立式」を考えることが有害であることをはっきり毎回言って欲しいと思います。
算数教育界ではそもそも「式」の概念自体が非常識なので、学校関係者と算数の話をするときには相手が「式」という言葉を使っていても、非常識な意味で「式」という言葉を使っている可能性を疑う必要があります。

算数で子供達の多くが非常識な「式」概念を強制的に学ばされています。続く
Read 4 tweets
27 Mar
#Julia言語 配列に関するforループ3題

gist.github.com/genkuroki/94a4…

①整数の和

for x in A
@ inbounds for i in eachindex(A)

よりも

for i in eachindex(A)

が遅い。配列の要素に A[i] の形式でアクセスする場合には論理的なデバッグが終わった後に @ inbounds を付けると速くなる。 Image
#Julia言語 配列に関するforループ3題

gist.github.com/genkuroki/94a4…

②Float64の和

@ simd for x in A
@ inbounds @ simd for i in eachindex(A)

は速いが後者から@ inbounds @ simdの片方を削除するとかなり遅くなる。 Image
#Julia言語 配列に関するforループ3題

gist.github.com/genkuroki/94a4…

③配列にはメモリオーダーでアクセスした方が速い。

2次元配列 A[i, j] の話をforループで計算する場合には、

for j in axes(A, 2)

を外側に

for i in axes(A, 1)

を内側にするべきである。これを逆にするとかなり遅くなる。 Image
Read 5 tweets
26 Mar
#Julia v1.6.0の公式バイナリが

julialang.org/downloads/

からダウンロード可能になっていて盛り上がっていますね。

新機能を2つ紹介

② sum系の函数で「初期値」を指定できるようになった。空の和もエラーにならないようにできます。

sum(x^3 for x in 1:0; init=0)
→0

これ結構重要。
#Julia言語 v1.6.0

②マクロ版ではなく、函数版sprintfが

using Printf
Printf.format(Printf.format"%15.10f", π)

のように使えるようになりました。

これで実行時可変なフォーマットのprintfを容易に使えます。
#Julia言語 Julia言語では、コンパイル時に与えられたフォーマット専用のコードを生成する爆速のマクロ版printfがデフォルトで使えていました。C版のprintf函数より速い。

しかし、フォーマットが固定されるので不便な場合がたまにありました。v1.6.0以降は使い分ければよいということになります。
Read 4 tweets
25 Mar
#数楽

多項式f∈ℝ[x]に「a∈ℝをf(a)∈ℝを対応させる函数」を対応させる写像は単射なので、多項式fとそれに対応する実数の函数を区別しなくも大丈夫。(無限体でもOK)

有限体F上の多項式g∈F[x]に「a∈Fをg(a)∈Fを対応させる函数」を対応させる写像は単射でないので、それらを同一視できない。
#数楽 例えば二元体𝔽₂={0,1} (1+1=0)について、𝔽₂上の多項式としてxとx²は異なるが、𝔽₂上の函数としてはどちらも恒等写像になって等しくなってしまう。

xとx²を𝔽₂の2次拡大𝔽₄=𝔽₂[α] (α²=α+1)上の函数とみなしたものは互いに異なる。

こういう具体例がノータイムで出て来ることが大事。
#数楽 面倒なのは有理函数(多項式環の分数体の要素)に対応する「函数」の場合。

有理函数ごとに定義域も変わるので、異なる定義域を持つ函数達を同時に扱うための「処理」が必要になる。(これには複数の処方箋がある。)

結果的に、無限体の場合には、有理函数と対応する「函数」は同一視可能になる。
Read 78 tweets
25 Mar
大学生相手であっても、必要な数学の実力は結構高いので、統計学を教えるのに苦労しています。

高校生相手に検定が「お墨付きが得られる道具」であるかのように教えられてしまうようになったら最悪。

あと、信頼区間がモデル依存であることも(大学生と同様に)教えることにならないと思う。
現実には世界的にかなり悲惨なことになっていて、論文を日常的に書いている研究者であっても、統計的検定を「お墨付きが得られる道具」扱いしている人達が沢山いるんじゃないか?

そういう現状は若くて優れた研究者が育つことを妨害していると思う。

こういう問題を維持固定しないような教育が必要。
まだ高校生なのに、「統計的に有意である!」を水戸黄門的な「ひかえおろう!」と同じ意味で使うようになったら最悪(笑)
Read 24 tweets
25 Mar
#Julia言語 リポジトリの方の公式マニュアルに以下が追加されましたね。

* 引数の型の過剰な制限はよくある間違いです。疑わしいなら引数の型を書くのをやめましょう。

* Juliaでは戻り値の型宣言はほとんど使われません。一般に「型安定」な函数を書くべきです。

github.com/JuliaLang/juli…
#Julia言語 具体的には

fib(n::Int) = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2)

はよくある間違で、

fib(n)::Int = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2)

も誤りです。どちらでもBigIntによる計算が不可能になる。よく分からないなら

fib(n) = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2)

でよい。
#Julia言語 無用に引数の型を制限してしまううようだと、NASAでは仕事をできなくなります。(NASAでもJuliaを使っている)
Read 11 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!