Consider the following data structure, representing a tree with levels which increase but are not necessarily consecutive:
data MyTree (n :: T) where
MyLeaf :: MyTree n
MyNode :: Plus n m z => [MyTree ('Succ z)] -> MyTree n
where T
represents the Peano numbers at the type level, defined as
class Plus (n :: T) (m :: T) (r :: T) | r n -> m
instance Plus 'Zero m m
instance Plus n m r => Plus ('Succ n) m ('Succ r)
It is pretty easy to construct trees like
myTreeOne :: MyTree ('Succ 'Zero)
myTreeOne = MyNode ([ MyLeaf ] :: [MyTree ('Succ ('Succ 'Zero))])
myTree :: MyTree 'Zero
myTree = MyNode [ MyLeaf, myTreeOne ]
or
myLeafTwo :: MyTree ('Succ ('Succ 'Zero))
myLeafTwo = MyLeaf
myOtherTree :: MyTree 'Zero
myOtherTree = MyNode [ myLeafTwo ]
Now I would like to define the following function:
myTreeComponents MyLeaf = []
myTreeComponents (MyNode components) = components
which just extracts the list of immediate subnodes of the tree, but I'm not able to write it's correct type.
This is the error I get
• Couldn't match expected type ‘p’ │
with actual type ‘[MyTree ('Succ z)]’ │
because type variable ‘z’ would escape its scope │
This (rigid, skolem) type variable is bound by │
a pattern with constructor: │
MyNode :: forall (n :: T) (m :: T) (z :: T). │
Plus n m z => │
[MyTree ('Succ z)] -> MyTree n, │
in an equation for ‘myTreeComponents’ │
at src/Model.hs:159:19-35 │
• In the expression: components │
In an equation for ‘myTreeComponents’: │
myTreeComponents (MyNode components) = components │
• Relevant bindings include │
components :: [MyTree ('Succ z)] (bound at src/Model.hs:159:26) │
myTreeComponents :: MyTree n -> p (bound at src/Model.hs:158:1) │
| │
159 | myTreeComponents (MyNode components) = components │
| ^^^^^^^^^^
With dependent type languages it should be something like
exists m. Plus n m z => MyTree n -> [ MyTree ('Succ z) ]
Is it possible to write such a type in Haskell? Otherwise how should I write my function?
data
GADT. It will look asMyTree
but only with the second constructor. It is very tricky, though, to extractm,z
from the first type to use in the existential: probably, it would be easier if you can add a proxy toMyNode
constructor. (This is quite painful. If Haskell had real dependent types, or better ambiguous types, it would be easier.) – chi