Я пишу компилятор, в котором я использую GADT для своего IR, но стандартные типы данных для всего остального.У меня возникают проблемы при преобразовании старого типа данных в GADT.Я попытался воссоздать ситуацию с меньшим / упрощенным языком ниже.
Для начала у меня есть следующие типы данных:
data OldLVal = VarOL Int -- The nth variable. Can be used to construct a Temp later.
| LDeref OldLVal
data Exp = Var Int -- See above
| IntT Int32
| Deref Exp
data Statement = AssignStmt OldLVal Exp
| ...
Я хочу преобразовать их в этот промежуточныйform:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
-- Note: this is a Phantom type
data Temp a = Temp Int
data Type = IntT
| PtrT Type
data Command where
Assign :: NewLVal a -> Pure a -> Command
...
data NewLVal :: Type -> * where
VarNL :: Temp a -> NewLVal a
DerefNL :: NewLVal ('PtrT ('Just a)) -> NewLVal a
data Pure :: Type -> * where
ConstP :: Int32 -> Pure 'IntT
ConstPtrP :: Int32 -> Pure ('PtrT a)
VarP :: Temp a -> Pure a
На данный момент я просто хочу написать преобразование из старого типа данных в новый GADT.Сейчас у меня есть нечто, похожее на это.
convert :: Statement -> Either String Command
convert (AssignStmt oldLval exp) = do
newLval <- convertLVal oldLval -- Either String (NewLVal a)
pure <- convertPure exp -- Either String (Pure b)
-- return $ Assign newLval pure -- Obvious failure. Can't ensure a ~ b.
pure' <- matchType newLval pure -- Either String (Pure a)
return $ Assign newLval pure'
-- Converts Pure b into Pure a. Should essentially be a noop, but simply
-- proves that it is possible.
matchType :: NewLVal a -> Pure b -> Either String (Pure a)
matchType = undefined
Я понял, что не могу написать convert
тривиально, поэтому я попытался решить проблему, используя эту идею matchType
, которая действуеткак доказательство того, что эти два типа действительно равны.Вопрос в том, как мне написать matchType
?Это было бы намного проще, если бы у меня были полностью зависимые типы (или мне так сказали), но могу ли я закончить этот код здесь?
Альтернативой этому было бы как-то предоставить newLval
в качестве аргументаconvertPure
, но я думаю, что по сути просто пытается использовать зависимые типы.
Любые другие предложения приветствуются.
Если это поможет, у меня также есть функция, которая может преобразовать Exp
или OldLVal
для его типа:
class Typed a where
typeOf :: a -> Type
instance Typed Exp where
...
instance Typed OldLVal where
...
РЕДАКТИРОВАТЬ:
Благодаря превосходным ответам ниже, я смог закончить написание этого модуля.
В итоге я использовал пакет singletons
, упомянутый ниже.Сначала это было немного странно, но я нашел, что это довольно разумно использовать после того, как я начал понимать, что я делаю.Однако я столкнулся с одной ловушкой: для типов convertLVal
и convertPure
требуется экзистенциал для выражения.
data WrappedPure = forall a. WrappedPure (Pure a, SType a)
data WrappedLVal = forall a. WrappedLVal (NewLVal a, SType a)
convertPure :: Exp -> Either String WrappedPure
convertLVal :: OldLVal -> Either String WrappedLVal
Это означает, что вам придется развернуть этот экзистенциал в convert
,но в остальном ответы ниже укажут вам путь.Большое спасибо еще раз.