名前空間は名前とクラスのリストで構成されます クラスは名前とメソッドのリストで構成されます メソッドは名前、戻り値の型と引数のリストで構成されます 引数は型と名前で構成されます この単純なプログラムの例言語:
型システムの型定義も必要であり、それが実際に共用体型が価値のある唯一の場所です。
type Type = Void | Int | String
したがって、言語の型は int または string または void のいずれかですが、何も (null など) にすることはできず、これらのオプションの複数にすることはできません。
名前空間の型は、次のように完全に匿名にすることができます。
string * (string * (Type * string * (Type * string) list) list) list
サンプルの名前空間を次のように定義できます。
"ns", ["cls1", [Void, "m1", []]
"cls2", [Void, "m2", [Int, "i"; String, "j"]]]
実際には、おそらく、名前空間を別の名前空間に配置したり、クラスをクラスに配置したりして、コードを次のように進化させたいと思うでしょう。
type Type =
| Void
| Int
| String
| Class of Map<string, Type> * Map<string, Type * (Type * string) list>
type Namespace =
| Namespace of string * Namespace list * Map<string, Type>
Namespace("ns", [],
Map
[ "cls1", Class(Map[], Map["m1", (Void, [])])
"cls2", Class(Map[], Map["m2", (Void, [Int, "i"; String, "j"])])])
匿名型は、混乱の元にならない限り問題ありません。経験則として、2 つまたは 3 つのフィールドがあり、それらが異なるタイプ (ここでは「メソッド」のように) である場合は、タプルで問題ありません。それ以上のフィールドまたは同じタイプのフィールドが複数ある場合は、レコード タイプに切り替える必要があります。
したがって、この場合、メソッドのレコード タイプを導入することができます。
type Method =
{ ReturnType: Type
Arguments: (Type * string) list }
and Type =
| Void
| Int
| String
| Class of Map<string, Type> * Map<string, Method>
type Namespace =
| Namespace of string * Namespace list * Map<string, Type>
Namespace("ns", [],
Map
[ "cls1", Class(Map[], Map["m1", { ReturnType = Void; Arguments = [] }])
"cls2", Class(Map[], Map["m2", { ReturnType = Void; Arguments = [Int, "i"; String, "j"] }])])
そしておそらく、それらのレコードを構築するためのヘルパー関数:
let Method retTy name args =
name, { ReturnType = retTy; Arguments = args }
Namespace("ns", [],
Map
[ "cls1", Class(Map[], Map[Method Void "m1" []])
"cls2", Class(Map[], Map[Method Void "m2" [Int, "i"; String, "j"]])])