Since we’re into making predictions, I predict this’ll replace one problem by a different one. the reason for product types is to put together values that belong together/change at the same time/depend on the same triggers. (This is just tedious old-hat relational theory again: “same triggers” = keys/functional dependencies – that is relational FDs, not Haskell ones.)
domain concepts aren’t always represented at type-level; [section 4.1 again]
If a domain concept is a config flag, you could represent each flag as a type, with each setting as a possible data constructor. But now some flags ‘belong together’: do you really want separate handing for FlexibleInstances
, FlexibleContexts
, UndecidableInstances
, FunctionalDependencies
, Overlap*
? They’re likely to be needed in all the same places.
And if (groups of) DynFlags
get overridden for some sub-purpose but the type system knows only the type of the flag, how to type-safely distinguish the ‘global’ flag from its local override?
The trouble with Haskell records is field handling isn’t “represented at type-level”. Consider some Hugs/Trex:
gbSwap ( g = g, b = b | rest ) = ( g = b, b = g | rest )
-- inferred :: (a\g, a\b) => Rec (g :: b, b :: c | a) -> Rec (b :: b, g :: c | a)
This type shows gbSwap
takes a record argument; returns a record result; touches exactly two fields in the record named g, b
; expects the record to contain other fields rest :: a
, but doesn’t touch them/just passes them back. (Yes, Trex’ display of that type is unfriendly [**]: I’m not advocating for exactly Trex.)
So the field names participate in the types and in effect reveal which functions read and/or write which fields. This is both more lightweight code and better documenting of the function’s behaviour.
[**] Trex follows H98 in making field names lower-case. But they’re constants, not variables. (Compare HasField
represents field names as type-level String
.) So that Rec( b :: b, ...)
means there’s a field name b
of type (variable) b
. Note that field names are in a different namespace vs data
constructors/variables vs type
constructors/variables, so the human is confused, not the compiler. I’d spell field names starting Upper, like other constructors. In rest :: a
, rest
is a regular data
variable, a
is its regular type variable, so both correct to be lower-case.)