In this post, I explored two different variations (nested type tags and extensible records) and came to a (probably unsurprising) conclusion that extensible records are more flexible.
Extensible records make it possible to get the compiler to enforce that only supported attributes are passed in a list.
But ideally, I’d like to be able to enforce two types of constraints on a list of values given to a function:
- required values are supplied in the list
- only supported values are supplied.
This would allow for a nice API in situations where, for example, a function takes a list of attributes or a list of child elements etc.
However, I wasn’t able to find a way of enforcing both of those with phantom types, so my current thinking is that the best solution is to have two arguments: a record with required attributes as the first argument followed by a list of optional attributes which the compiler rejects if they’re not supported by the function according to phantom type tags. This is similar to the
elm-ui API in
Element.Input, for example (minus the phantom types).
Is it possible to have a better API than this? I’d be keen to hear of ways to enforce more compile-time checks.