Extracting type metadata from Elm code?

Ah, thanks. So, the same limitation affects elm-serialize:

semaphoreCodec : S.Codec Semaphore
semaphoreCodec =
    S.custom
        (\redEncoder yellowEncoder greenEncoder value ->
            case value of
                Red i s b ->
                    redEncoder i s b

                Yellow f ->
                    yellowEncoder f

                Green ->
                    greenEncoder
        )
        -- Note that removing a variant, inserting a variant before an existing one, or swapping two variants will prevent you from decoding any data you've previously encoded.
        |> S.variant3 Red S.int S.string S.bool
        |> S.variant1 Yellow S.float
        |> S.variant0 Green
        -- It's safe to add new variants here later though
        |> S.finishCustom

In fact, even the relatively simple “codec-style Route” code I posted earler had a bug - routeParser didn’t handle the User and Task routes… I wasn’t careful enough!

(aside: that we don’t have to be so careful with languages like Rust and Elm is a big part of their appeal; features that require “you have to be careful!” compel me to seek alternatives)

So it seems fundamental that within Elm, there’s no way to statically enforce the symmetry of encoder/decoder pairs. From outside Elm, there seems to be two options:

Generate the Routes, code, and tests from data external to the Elm code

  • Would have to write the Route type(s) indirectly, using the generator tool rather than plain Elm.
  • Existing editor tooling wouldn’t help while writing the routes?
  • Would probably be specific to Route-like structures; might not be applicable to general symmetry problems.
  • Hard to convert an existing codebase to using the generated code

Extract the Route type information from existing Elm code, generate the tests from that

  • Lets us keep writing Routes in plain Elm, like usual
  • Should be low-effort to set up; tell it where to find the Type and the two functions, and what to name the test, that’s it?
  • Easy to tack onto existing codebases
  • May be harder to “extract” the Type from existing code; e.g., crossing module boundaries, nested types (elm-syntax gives the AST, but that may be too low-level… would we need to recreate a subset of the compiler?)

Both approaches might need some way to limit unbounded types like Int, String (and types that wrap those)… how much could we do for the general case? How much would the user need to tell the generators about how to generate their own unbounded types?