Glispは構文上型と値を区別しない。
たとえば、TypeScriptではある型Tからなる配列型をT[]
と表現する。これは通常の値からは区別された文法を持ち、値が置かれる場所にconst Atrr = T[]
のように型を置くことができない。型と値は名前空間も独立しており、文法要素として直交している。これは、トランスコンパイルすることで型情報を削ぎ落とし、JavaScriptとして実行されることを想定されているがゆえの要請。
Glispでは極力実装や文法をシンプルにしたかったので、こうした区別をつけないこととした。すべての値は型であり、すべての型は値として使うことができる。
TypeScript、Haskellなど多くの言語は、リスト型とタプル型を区別する。Glispではいずれも、「末尾の要素を可変長にできるタプル型」として統合して扱う。
(TypeScript) -> (Glisp)
T[] -> [...T]
[T, U] -> [T U]
[T, ...U] -> [T ...U]
おそらくは2個以上要素のあるであろう配列をタプルにキャプチャしたい時。rangeが返すであろうタプルの要素数を明示しないといけないので不便。rangeはまだ返すであろう要素数の計算が簡単でも、「指定範囲間の素数からなるリストを返す」なんてケースだと、型推論の計算コストが評価時の計算コストと同等になってしまう。
[a b] = (take 2 (range 0 100))
;; 右辺の型は [...Int] のため、左辺にマッチしない
特例として、可変長タプル <: 固定長タプル
という形の部分型判定は、固定長部分の要素数に依らず真とする。個数が足りるかどうかは実行時に判定し、足りない要素はボトムで埋める。
[o o] <: [o] ; 固定長タプル同士は、要素数が同か多い方が部分型
[o o] <: [o ...o] ; タプル <: 可変長タプル の場合も固定長部分を比較する
[o ...o] <: [o ...o] ;これも真
[...o] <: [o o] ; 可変長 <: タプル の場合の場合、
これ、結局タプルとリストを区別する普通の言語に戻っとりませんか?
あとこの場合
(match t ;; 型は [...T], 実際の値は []
[x] ... ;; ここにマッチしてしまう
_ ...) ;; 本当はここにマッチして欲しいのに
のようになりかねない
いや、そもそもこのケースはmatch構文で場合分けするべきじゃない。
type T = number
// Source has 3 element(s) but target allows only 2.
const v0: [T, T] = [1, 2, 3]
// OK
const [v0a, v0b] = [1, 2, 3]
// Type '[number, number, number]' is not assignable to type '[number, number]'
const [v0c, v0d]: [T, T] = [1, 2, 3]
// OK
const v1: [T, T] = [1, 2]
// Source has 1 element(s) but target requires 2.
const v2: [T, T] = [1]
// Target requires 2 element(s) but source may have fewer.
const v3: [T, T] = Array.of(1)
// Target allows only 2 element(s) but source may have more.
const v4: [T, T] = [1, 2, ...Array.of(1)]
// OK
const v5: [T, T] = Array.of(1) as [T, T]
// inferred as number[]
const v6 = [1, 2, ...Array.of(1)]
A == B
の時のみ、 const a = b
のように代入できる。