Decodal

Runtime Model

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

RuntimeValue

RuntimeValue =
  Concrete(ConcreteValue)
  Abstract(AbstractValue)

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

Decodal は unknownanynull のような「存在するが意味が未確定な値」を runtime value として持たない。 識別子や field が解決できない場合は、その場で diagnostic になる。 未解決値を後続の演算へ流して推論することはしない。

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, ObjectField>

ObjectField:
  value: ThunkId
  span: Span

例えば以下の 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<ConstraintEntry>
  default: Option<ThunkId>
}

ConstraintEntry {
  constraint: Constraint
  span: Span
}

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

port = Int default 8080;

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

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

これは以下である。

Concrete(Int(8000))

Runtime scope

Decodal runtime は application runtime ではなく、pure value evaluator である。 同じ source、同じ import sources、同じ host globals が与えられた場合、評価結果は決定的である。

runtime が扱う責務は以下に限る。

  • expression を評価する。
  • thunk を必要に応じて force する。
  • concrete / abstract value を合成する。
  • materialize 時に constraint を検証する。

runtime は filesystem、network、environment variable、time、random、mutation を扱わない。 core における import は host supplied source を受け取る境界であり、filesystem access ではない。

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                  外部へ出力可能な最終データ。