After reading @jfmengels’s post about using phantom types to single out an element of a type, I was curious to see if I could apply this technique to constrain a list of values passed as an argument.
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.