Мне нравится исправление Чи, потому что использование Proxy
s для хранения типов - хороший трюк под вашим поясом. В его ответе Proxy
используется для устранения неоднозначности, что m
использовать в ограничении Plus n m z
при вызове конструктора Nodes
. (На данный момент существует ограничение Plus n m z
в области видимости, но без прокси-сервера нет способа указать компилятору выбрать его.)
Помимо Proxy
, есть еще один способ исправить неоднозначные типы: устранить неоднозначный тип. В данном случае это m
. Таким образом, вместо класса Plus n m z
у нас будет класс LE n z
, который имеет все те же экземпляры, но нигде не упоминает m
. Итак:
{-# LANGUAGE GADTs, KindSignatures, DataKinds, TypeFamilies,
MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}
{-# OPTIONS -Wall #-}
data T = Zero | Succ T
class LE (n :: T) (z :: T)
instance LE 'Zero n
instance LE n z => LE ('Succ n) ('Succ z)
Есть одна морщина. В ответе Чи он использует слегка измененное определение Plus
, которое дает экземпляр Plus n 'Zero n
бесплатно, но в вашей формулировке это недоступно, и, следовательно, LE n n
не предоставляется бесплатно. Поэтому нам придется немного украсить наши листья.
data MyTree (n :: T) where
MyLeaf :: LE n n => MyTree n
MyNode :: LE n z => [MyTree ('Succ z)] -> MyTree n
Все бетонные деревья такие же, как были раньше (не нужно никаких Proxy
).
myTreeOne :: MyTree ('Succ 'Zero)
myTreeOne = MyNode [ MyLeaf :: MyTree ('Succ ('Succ 'Zero)) ]
myTree :: MyTree 'Zero
myTree = MyNode [ MyLeaf, myTreeOne ]
myLeafTwo :: MyTree ('Succ ('Succ 'Zero))
myLeafTwo = MyLeaf
myOtherTree :: MyTree 'Zero
myOtherTree = MyNode [ myLeafTwo ]
И единственное отличие в myTreeComponents
по сравнению с ответом Чи (помимо стирания Proxy
) состоит в том, что нам нужно ввести переменную типа в область видимости для использования в теле.
data Nodes (n :: T) where
Nodes :: LE n z => [MyTree ('Succ z)] -> Nodes n
myTreeComponents :: forall n. MyTree n -> Nodes n
myTreeComponents MyLeaf = Nodes ([] :: [MyTree ('Succ n)])
myTreeComponents (MyNode components) = Nodes components