openlang

A discriminated union starts after the response check

Partly reviewed

An external response begins as bytes, before the application has any reason to call it a particular variant. In a hypothetical client, text decoding and JSON parsing come first, with their own failure paths. The parsed value should then enter the validation step as unknown.

Suppose the supported response has a ready tag with an items array of strings, or a failed tag with a message string. The runtime check must establish that the value is an object, that its tag is recognized and that the corresponding payload has the required shape. An object carrying ready without items is incomplete. One carrying waiting has a tag outside this contract. Neither becomes valid because a type assertion names the intended union.

After a successful check, the decoder can return a discriminated union. A branch testing for ready then narrows the value so the application can use its items. An exhaustive check with never helps expose an unhandled alternative in the declared model. That is useful maintenance information, but it does not inspect response bytes.

The client also needs a policy for rejected input. It could report a decoding failure, or deliberately model an unknown response as another variant. Quietly casting it into the accepted union skips that decision. I would keep the runtime evidence close to the construction of the typed value, then review each handler's calculation separately. Covering a branch does not establish that the branch produces the right answer.

The conversation continues 2

Follow the reasoning
  1. Permalink

    Your separation between checking input and representing alternatives has another limit in Haskell. A hypothetical Either result can distinguish a decoding error from accepted data without proving evaluation terminates. Matching Right undefined merely to return True need not demand the payload. Asking for the length of that payload does demand it and encounters failed evaluation. An expression such as let loop = loop in loop need not reach either constructor. The alternatives describe possible forms, not totality.

  2. Permalink

    Suppose that service later introduces Deferred(ticket). A closed Scala 3 enum can give this case its own data, but adopting it means reviewing the decoder and consumers such as rendering and persistence. That coordination is useful when one model owns the alternatives. If independent producers must introduce variants without changing that central definition, the closed set becomes a cost. An open interface or an explicitly validated unknown envelope may fit better, with less exhaustiveness available centrally.