I have a nice way to wrap multiple type parameters into one.
data WorldModel = WorldModel Type Type Type
-- plus type families to extract them
I had a type synonym type PlainWorldModel = 'WorldModel Some Instantiation Here which works fine.
Now I want the parameterisation to be recursive
type PlainWorldModel = 'WorldModel Some Instantiation (Here PlainWorldModel) which I cannot obviously do with type synonyms, because of substitution.
How can I do it with newtype? I can't make the newtyped field be of type WorldModel, only of Type.
Do I need to bring in singletons?
Concisely,
type PlainWorldModel = 'WorldModel Some Instantiation Here -- this was fine
type PlainWorldModel = 'WorldModel Some Instantiation (HereParameterised PlainWorldModel) -- I want to do this and it is not fine
newtype PlainWorldModel = PWM ( 'WorldModel Some Instantiation (HereParameterised PlainWorldModel)) -- this does not work
I don't have a definite answer, but I am leaning towards this being impossible to achieve in Haskell.
A key feature of types in Haskell is that whenever we have
t :: kwherekis any kind, then the (type-level) expressiontshould be (strongly) normalizing. In other words we should be able to expand all type synonyms, all type families, etc. intand reach its normal form in a finite number of steps.There are a few culprits here that partially break this property.
UndecidableInstancesfor instance allows for infinitely recursing type families. HavingType :: Typeis also problematic. However, if we disregard those, normalization should hold.Now, what you want to achieve is general type-level recursion
type T = F Tfor someF. This is called "equi-recursion" and would break normalization and arguably should be prevented at all kinds.You might then wonder why in the kind
Typewe do allow recursion, then. Well, there we only allow "iso-recursion". We never have the strict equalityT ~ F Tbut we only allow the two distinct typesTandF Twhich "just happen" to be isomorphic, and provide the constructors/eliminators to express such isomorphism. This also makes our type system nominal (and not structural).Such recursion only happens at the kind
Typethough.My hunch is that, if we wanted to allow recursion in other kinds, we would need to have some form of "iso-recursion", so to ensure normalization. I am not completely sure about how that would work.