generated at
NimのConcept
Genericsの型パラメータに制約を与えるイメージ


例: Comparable conceptを定義
nim
type Comparable = concept x, y (x < y) is bool
Comparable は、 int 等と同様に一種の型として利用できる
Comparableな型同士は < によって比較することができることを示す
Comparable な型は、「<」で比較可能ならば int でも string でも何でも良い
利用例
nim
proc hoge(x,y: Comparable): bool = x>y echo(hoge(4, 2)) # Success echo(hoge('4', 2)) # Error
hogeの呼び出し時に型検査が行われる
4 , 2 はいずれもintであり比較可能でなので通る
'4' 2 はintとstringの < 比較なので失敗する
つまり、Conceptsの話を一旦忘れて、 echo(x < y) が実行てきてかつboolであるような x , y であるかどうかが検査される


Conceptsを使わない場合にどうなるか
以下のような乗算ができることを示すConceptを定義する
nim
type Mul = concept x x * x
乗算できるような型なら Mul に適用できる
普通にGenericsで関数の型を定義した場合に、かなり制約が弱まってしまう
nim
proc mul[T](arr: seq[T]): T = foldl(arr, a * b)
これは seq[T] 型の値を引数に取るが、これだけ見ると、 seq[string] seq[int] も許容されてしまう
実際には string * string という演算はできないので、これは型安全ではない
nim
echo hoge @['1','2','3','4','5'] # Error echo hoge @[1,2,3,4,5] # Success
このエラーは型検査時ではなく、実行時に起きている
と思ったが、コンパイルエラーになった。
どういう仕組みだmrsekut
なのでconceptを使う
↑genericsでもエラーになったので、ここは「なので」ではないmrsekut
nim
proc hoge(arr: Mul): Mul = foldl(arr, a * b) echo hoge @['1','2','3','4','5'] # 型検査でエラー(?)
char型は Mul 制約を満たしていないのでエラーになる
これは理想mrsekut



いろいろな定義例
x + で演算できる制約を示す
nim
type Addable = concept x x + x
プロシージャ制約 ref
nim
Bowable = concept x bow(x) is string # プロシージャの制約 x.name is string # フィールドの制約. xはnameを持っている
プロシージャ制約ってなに #??
Concepts内で関数を使用できるというわけではないのか?
できるならTypeScriptのTypeGuard的なことができるのでは


Conceptとinterfaceの違い
Interface
ある型が提供するメソッドの集合
Concept
ある型が満たすべき要求の集合
interfaceのように、「あるメソッドをもつ」こと規定したり、
「その型の変数を用いたある式が有効である」ことを規定したり。
polymorphismがない
Interface
run-timeのポリモーフィズム
Concept
compile-timeのポリモーフィズム



型クラスとの違い
ぜんぜん違う
そもそもConceptsは既存の型を拡張していない
既存の型が持っている性質を減らして制約を強めている感じ


関連
だいたいおなじ


参考




C++のconceptについて
]



Nim
# これでも動くわけだが、Personクラス(?)にgreetingメソッドを持つことを明示したい type OutputStream = concept var s s.greeting(string) type Person = object name: string proc greeting(self: Person, greeting: string) = echo greeting, self.name var p = Person(name:"hanako") p.greeting("good morning ") # ...うまく実装できなかった

Nim
# https://stackoverflow.com/questions/37550948/how-do-i-use-a-concept-in-nim type T = concept t t.a is string T0 = ref object a: string T1 = ref object a: string # ←コメントアウトするとコンパイルエラー q: string proc echoT(t: T) : void = echo "hello " & t.a echoT(T0(a: "T0")) echoT(T1(a: "T1", q: "q"))
Interfaceと何が違うのか

動く例
Nim
type CanDance = concept x x.dance # `x`はdanceというprocedureを持っている # --- type Person = object Robot = object proc dance(p: Person) = echo "People can dance, but not Robots!" # --- let p = Person() let r = Robot() proc doBallet(dancer: CanDance) = # `dancer` can be anything that `CanDance` dance(dancer) doBallet(p) # pはdanceメソッド(?)持っているので「People can dance, but not Robots!」と出力される # doBallet(r) # rはdanceメソッド(?)を持っていないのでコンパイルエラーになる

途中
Nim
# https://gist.github.com/honewatson/583135c1b191119a3b3be3fdbfe8607b import strutils, strformat type Dispatch = enum Reveal IKind = enum Human, NonHuman ObIkind = ref object of RootObj kind: IKind Person = object of ObIkind name: string Robot = object of ObIkind name: int Place = object of ObIkind description: string Data = concept x toString(x) x.kind is IKind # We can use a generic proc as a starting point to satisfy `Data` concept proc `toString` proc toString[T](data: T): string = return fmt"{data.name} is a {data.kind}" # We can provide a specific `toString` proc for place which does not have a property of `name` proc toString(place: Place): string = return fmt"{place.description} is a {place.kind}" var d1 = Person(kind: Human, name: "Jim") d2 = Place(kind: NonHuman, description: "Hill") d3 = Robot(kind: NonHuman, name: 738191) # Here we create a proc actions which has takes concept of type Data as one of the parameters # The concept of type Data is essentially a kind of restricted generic # This should work for both the C and JS Backends proc actions(dispatch: Dispatch, data: Data): string = case dispatch: of Reveal: data.toString() echo actions(Reveal, d1) # Jim is a Human echo actions(Reveal, d2) # Hill is a NonHuman echo actions(Reveal, d3) # 738191 is a NonHuman
TypeScript
enum Dispatch { Reveral } enum IKind { Human, NonHuman } class ObIkind { kind: IKind; } class Person { constructor(public kind: IKind, public name: string) {} } class Robot extends ObIkind { constructor(public kind: IKind, public name: number) { super(); } } class Place extends ObIkind implements Data { constructor(public kind: IKind, public description: string) { super(); } toString() { return `${this.description} is a ${this.kind}`; } } interface Data { toString: (x) => string; kind: IKind; } const d1 = new Person(IKind.Human, 'Jim'); const d2 = new Place(IKind.NonHuman, 'Hill'); const d3 = new Robot(IKind.NonHuman, 738191); const actions = (dispatch: Dispatch, data: Data) => { switch (dispatch) { case Dispatch.Reveral: data.toString(); default: break; } };

わからん
Nim
type Value = concept v v.value is bool # bool型のプロパティ`value`を持っている MinValue = concept v v is Value v.minVal is bool # bool型を返すminValメソッドを持っている MaxValue = concept v v is Value v.maxVal is bool type BoolParam = ref object value: bool proc set(self: Value, v: bool): bool = # following block only compiled when expr is true when self is MinValue: if v < self.minVal: return false when self is MaxValue: if v > self.maxVal: return false self.value = v return true let b = BoolParam(value: false) echo b.set(true) echo b.value


Conceptsを使わずともDuck Typingができる
どんな機能が提供されなければならないかをより明確にしたいときはconceptsを使う
Nim
proc energy[PhysicalObject](o: PhysicalObject): float = 0.5 * o.mass * o.speed * o.speed # プロパティとして,mass,speedを持つ type SimpleObject = object mass: float speed: float # メソッドとしてmass,speedを持つ type ComposedObject = object proc mass(self: ComposedObject): float = 42 proc speed(self: ComposedObject): float = 42 let simple = SimpleObject(mass: 1.0, speed: 5.0) echo simple.energy # 0.5 * 1.0 * 5.0 * 5.0 = 12.5 let composed = ComposedObject() echo composed.energy # 0.5 * 42 * 42 * 42 = 37044.0
このコードはGenericsが良い仕事をしているのがわかる
試しに1行目の角括弧を消すとコンパイルエラーになる

動かん








Nimにinterfaceがないならタプル使ってinterfaceっぽっくしようぜ!



利用例 その1
nim
type Comparable = concept x, y (x < y) is bool # xとyは比較できる # 利用例 proc hoge(x,y: Comparable): int = return y echo hoge(4, 2) echo hoge('4', 2) # コンパイルエラー!!







参考
Is there interfaces in NIM language? - Nim forum