Skip to content

Wintermute 0.3.5 — plain-value type switch

Choose a tag to compare

@bsmr bsmr released this 17 Jul 10:33

Generalize the type switch from the otp.Receive() operand (0.3.4) to any value
operand: switch V := X.(type) now lowers to an Erlang case X of {tag, Field…} -> body; … end, the value-branching counterpart to the receive dispatch.

  • Struct-typed cases only, reusing the shared clause builder (tagged tuple
    {tag, Field…}, per-clause bound-field scope). A new emitTypeSwitch entry
    point dispatches on the operand: otp.Receive()receive, any other value →
    case X of.
  • Default required for the value form: a value matching no case falls through
    in Go (ordinary control flow), which a total Erlang case cannot express, so a
    default-less value switch is rejected rather than silently mis-transpiled (the
    default becomes the trailing _ ->). The receive form keeps default optional (a
    selective receive blocks on a non-match — it never falls through).
  • Operand and alias are uppercase Erlang variables (V := M.(type), M any); a
    lowercase operand is rejected, never silently emitted as an atom.
  • V.Field access resolves to the bound field variable; a bare alias V is
    rejected (whole-value passing deferred).
  • terminates() distinguishes the two forms: a value type switch terminates only
    with a default (a value case falls through in Go without one), so a
    default-carrying one may be a bare-if then-branch; a receive terminates without
    a default (it blocks, never falling through).
  • Runnable rung: testdata/typeswitch/classify.go transpiles, compiles with
    erlc, and RUNS — {ping, 1} and {pong, 2} each fire their clause.

Core is a behaviour-preserving refactor: the 0.3.4 receive clause loop was
extracted into emitTypeSwitchClauses, now shared by both the receive and the
value wrapper.

Deferred to 0.3.6+ (all error loudly): non-struct cases (→ guards), multi-type
cases, whole-alias V, tagless switch, init statement.