Twitterでふぁぼったものをひたすら試します

genericsとassociated typeとduck typing(とSwift)

いつもこの辺の話を忘れちゃうのでメモ。

Why does Go not have generic types?

github.com

Goに総称型がないっていうネタと議論を永遠に続けるために導入してないんじゃ

理由はFrequently Asked Questions (FAQ) - The Go Programming Languageにある通りだが、厄介なのはissueで延々と議論を続けているところである。やっぱり議論がしたいだけなのでは。

もう少しまとまった説明がproposal/15292-generics.md at master · golang/proposal · GitHubにあるのでこっちを読んだ方が幸せ。そこでは

Generics affect the whole standard library. It is desirable to have the standard library make effective use of generics. Every existing package should be reconsidered to see whether it would benefit from using generics.

などいろいろなことが述べられている。

Swiftの標準ライブラリが1番associated typeの恩恵を受けている気がする。ていうかもはや奴がgeneric protocolを使う姿を想像できない。

golangGenerics と私」

mattn.kaoriya.net

まあこれ自体は置いといて、Swiftには数値関係のprotocolがたくさんある。なんなら Numeric という例で出てきたものと同じ名のprotocolまであるがどうか。

Swift標準ライブラリにはprotocolで構成される巨大な継承ツリーがある。その中から数値型を抜粋するとだいたいこうなる:

f:id:S_Shimotori:20170802214546p:plain

Swiftの数値型の元締めが問題の Numeric である*1Numeric は二項演算能力(ただし和差積のみ)を指す。名前が一致しているしこいつが全部解決しそうしない。
なぜなら NumericSelf 同士の、つまり同型同士の二項演算能力を指すからである。 Int / UInt / Float / Double のごった煮の和は管轄外。

演算子システムもしっかり型を限定してくれる Self 先生も好きです(ほんと)

Int / UInt / Float / DoubleMyNumber を実装するのがだるいなら Numeric 1人に実装すれば解決するしない。宣言済みのprotocolに後から別のprotocolを継承することはできない。mattn氏の例と同様に全ての数値型で実装する必要がある。頑張りましょう。

Java の Generics にもの思い - QiitaJavaでは <T extends Number> を利用しているが、Javaの数値参照型の元締め Number

抽象クラスNumberは、プリミティブ型byte、double、float、int、long、およびshortに変換可能な数値を表すプラットフォーム・クラスのスーパークラスです。

で我らが Numeric とは別物である。よってこのJavaの例をそのままSwift語に書き換えてもうまくいかない。

Number (Java Platform SE 8)

Swift: Why Associated Types?

www.russbishop.net

Russ Bishop氏はAppleの中の人らしい。

記事は2015年のもの。現在の Collection が持つassociatedtypeは

  • associatedtype IndexDistance = Int
  • associatedtype Iterator = IndexingIterator<Self>
  • associatedtype SubSequence
  • associatedtype Indices

である。いずれにしてもこれを総称型パラメータでやったらヤバイよねっていう話。 https://github.com/apple/swift/blob/781f6326bd9932356086874baa12c812d100565e/stdlib/public/core/Collection.swift

Scalaはtraitでもgenericsとabstract typeを両方使えるので、この記事はOdersky大先生のScalaインタビューを引用している。こちらでもパラメータ数の爆発の可能性が欠点として挙げられている。

The Purpose of Scala's Type System

You could parameterize class Animal with the kind of food it eats. But in practice, when you do that with many different things, it leads to an explosion of parameters, and usually, what’s more, in bounds of parameters.

Introducing Abstract Base Classes

www.python.org

29.7. abc — 抽象基底クラス — Python 3.6.1 ドキュメント

Pythonは基本的にはduck typingなのだがAbstract Base Classesなるものが存在している。記事の最後ではABCとduck typing、generic function、interfaceとの比較がされている。

Python 3.4.0 の新機能 (3) - Single-dispatch generic functions - Qiita

JavaScalaC#ジェネリクス機能比較表

kmizu.hatenablog.com

よっしゃ!Swiftも参戦だ!

ジェネリックなクラス
class MyList<T> {}
ジェネリックなメソッド
func id<T>(arg: T) -> T { return arg }
型パラメータの上限
func max<T: Comparable>(a: T, b: T) -> T {
    return a < b ? b : a
}
型パラメータの下限

なくない?

型パラメータの推論
id(arg: "foo")  // "foo"
全ての型のサブタイプ(ScalaにおけるNothing)

こいつもなくない?

scala.Nothingは何のためにあるのか - kmizuの日記

definition-site variance

記憶にない

use-site variance

記憶にございません

実行時における型パラメータの扱い

(実行時のことは知らん)

型パラメータをnewする

え、無理でしょ( error: non-nominal type 'T' does not support explicit initialization )

Nominal and Structural Type Systems - soutaroブログ
Swiftに息づくstructural types(構造的型) - Qiita

同じジェネリッククラスを複数同時に継承する

多重継承はダメだし総称型protocolなんて概念はこの世には存在しない

同じ名前で型パラメータの個数が異なるジェネリッククラスを複数作成する

同じ型と認識されるので無理( error: invalid redeclaration of 'Tuple' )

高階のジェネリクス(Type Constructor Polymorphism)

何これ。

「filterを実装しておけばrejectが勝手に出来上がってる」みたいなやり方はよくあるが。

ジェネリックな型エイリアス
typealias StringDictionary<T> = Dictionary<String, T>

let d: StringDictionary<Int> = ["x": 50, "y": 100]

まとめ

みんな違ってみんないい

*1:Strideableも全員に該当する性質だが、こいつはUnsafePointer軍団も監督しているので数値限定ではない。NSNumberもFoundationの子なので違う。