Decodal

Diagnostics and Fallback

エラーは runtime value ではなく diagnostic として扱う。 処理系はエラー内容に基づく汎用的な実行時分岐を提供しない。

Diagnostic

Diagnostic {
  kind: DiagnosticKind
  span: Span
  message: String
  labels: Vec<DiagnosticLabel>
}

DiagnosticLabel {
  span: Span
  message: String
}

span は primary location を示す。 表示時には Span.source を source id のまま出すのではなく、可能な限り file path や virtual file name に解決する。 labels は同じ error に関係する追加 location を示す。 合成や materialize の失敗では、衝突した constraint、value、default、または処理中 field path を label に含める。

代表的な diagnostic kind:

  • syntax error
  • unresolved identifier
  • type mismatch
  • constraint violation
  • composition conflict
  • default conflict
  • cycle dependency
  • import failure
  • match failure
  • materialization failure

エラーは値ではない

評価失敗は RuntimeValue ではなく Diagnostic を返す。 そのため、通常の式はエラー内容に基づいて分岐できない。

Result<RuntimeValue, Diagnostic>

これにより、制約違反、未定義識別子、循環依存、import 失敗などが通常値として流れることを避ける。

合成と materialize の diagnostic

合成や materialize の失敗は、以下を示す。

  1. どの段階で失敗したか: composition、patch、materialization
  2. どの field path を処理中だったか
  3. どの constraint、value、default が衝突したか
  4. なぜ合成または materialize できないか

例えば default が constraint を満たさない場合は、constraint の位置と default value の位置の両方を label として持つ。 object field の合成で concrete value が衝突する場合は、左辺 field と右辺 field の位置を label として持つ。

try / catch は core に入れない

汎用 try / catch は core に入れない。 エラーを制御フローとして扱うと、どの失敗を捕捉できるか、捕捉後の thunk state をどう扱うか、制約違反を握りつぶしてよいか、といった仕様が重くなる。

fallback は有限で明示的な仕組みに限定する。

  • default: 未指定値の fallback。
  • match: 有限 pattern に基づく分岐。

Decodal は unknown / any を持たないため、field 不在や未解決 identifier を fallback 可能な通常値として扱わない。 それらは diagnostic として報告する。