Decodal/doc/manual/souce/design/runtime-model.md

2.8 KiB

Runtime Model

処理系の評価結果は、具体値と抽象値を区別した runtime value として扱う。 valueconstraintsdefault を横並びに持つ構造にはしない。

RuntimeValue

RuntimeValue =
  Concrete(ConcreteValue)
  Abstract(AbstractValue)

Concrete は明示的な値である。 Abstract は、まだ具体値に確定していない制約付きの値である。

ConcreteValue

ConcreteValue =
  String(String)
  Int(i64)
  Float(f64)
  Bool(bool)
  Array(Vec<ThunkId>)
  Object(ObjectValue)
  Function(FunctionValue)

object は concrete structure として扱う。 ただし、各 field の中身は concrete value でも abstract value でもよい。

ObjectValue:
  fields: Map<Symbol, ThunkId>

例えば以下の schema object は、object 自体は concrete だが、field の値は abstract value になる。

MyConfig = {
    host = String;
    port = Int default 8080;
};

概念的には以下である。

Concrete(Object {
  host -> Thunk(Abstract { constraints: [String], default: none })
  port -> Thunk(Abstract { constraints: [Int], default: 8080 })
})

AbstractValue

AbstractValue {
  constraints: Vec<Constraint>
  default: Option<ThunkId>
}

defaultAbstractValue にだけ存在する。 明示的な concrete value がある場合、default は保持しない。

port = Int default 8080;

これは以下の runtime value になる。

Abstract {
  constraints: [Type(Int)]
  default: Some(Thunk(8080))
}
port = 8000;

これは以下である。

Concrete(Int(8000))

Constraint

constraint は concrete value とは別の型として扱う。

Constraint =
  Type(PrimitiveType)
  Compare(Op, Literal)
  Regex(Pattern)
  BuiltinPredicate(Symbol)
  ObjectConstraint(...)

初期実装では、object の形は主に Concrete(Object) の field に Abstract を置くことで表現する。 object 全体にかかる constraint は必要になった時点で追加する。

Data

materialize 後の出力可能な値は runtime value とは別型にする。

Data =
  String(String)
  Int(i64)
  Float(f64)
  Bool(bool)
  Array(Vec<Data>)
  Object(Map<Symbol, Data>)

Function、未解決の Abstract、未評価の thunk は Data にはならない。

命名

実装内部では RuntimeValue を短く Value と呼んでもよい。 ただし、materialize 後の出力値とは区別する。

推奨する区別:

RuntimeValue / Value  言語内部の評価結果。Abstract を含む。
ConcreteValue         明示的な具体値。
AbstractValue         constraint と default を持つ抽象値。
Data                  外部へ出力可能な最終データ。