I’ve recently updated a large codebase from 8.10.7 to 9.2.5, enabling -XNoFieldSelectors
and -XOverloadedRecordDot
everywhere, rewriting selectors to dot syntax where necessary.
During the process I had one observation which I think is worth some discussion. What I observed is that from the semantics / operational point of view, newtype accessors are not really fields. Consider the following types
newtype Key a = Key { unKey :: a }
newtype MyMonad r a = MyMonad { runMyMonad :: r -> IO a }
I must say that something was not feeling right when I had to type key.unKey
or m.runMyMonad env
. It feels much more natural to unKey key
, or runMyMonad m env
. Usually when I create a newtype, I think in terms of “actions” like “unwrap the type” or “run the monad”, not necessarily “get the unKey
field” or “access the run function of this monad”. A few times I explicitly turned field selectors on on a module that declares a newtype, to retain the previous behaviour.
Overall, it summarizes to the following opinion: newtypes are not really records, and maybe their unwrap functions should not be treated as selectors (in light of -X(No)FieldSelectors
extension). What are your opinions on this topic?