ГАДЦ, но не экзистенциальная количественная оценка - PullRequest
0 голосов
/ 27 апреля 2020

Следующий код, содержащий экзистенциальные типы, не компилируется

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ExistentialQuantification #-}
module TestGadt () where

-- |Symbol only for arithmetic
data ArithSym = AInt Int | ASym String
-- |Symbol for arithmetic or strings or anything else
data GSym x = GInt Int | GStr String | GOther x
data Expr x = EAdd (Expr x) (Expr x) | E0 x deriving Functor
-- |Concatenate the string literals
concatStrings :: Expr (GSym x)  -> Expr (GSym x)
concatStrings = undefined
-- |Add the integer literals
addInts :: Expr (GSym x)  -> Expr (GSym x)
addInts = undefined
-- |Do some transform according to the sizes
otherOpt :: (e -> Int) -> Expr e  -> Expr e
otherOpt symSize = undefined


-- |Configuration to optimize an expression with symbols of type e.
data OptConfig e = OptConfig {
  ocSymSize :: e -> Int,
  ocGSymIso :: forall e0 . (e -> GSym e0,GSym e0 -> e)
  -- ^ this should be existential
  }

-- |Optimize given the configuration
opt :: OptConfig e -> Expr e -> Expr e
opt oc =
  otherOpt (ocSymSize oc)
  . fmap (snd $ ocGSymIso oc)
  . addInts
  . concatStrings
  . fmap (fst $ ocGSymIso oc)


arithConfig32 :: OptConfig ArithSym
arithConfig32 = OptConfig {
  ocSymSize=const 4,
  ocGSymIso=(\case{AInt i -> GInt i;ASym s -> GOther s},
             \case {GInt i -> AInt i;GOther a -> ASym a;_-> error "unreachable"})
  -- XXX: ^ ""Couldn't match type ‘e0’ with ‘String’"" even though e0
  -- should be existential!
  }

arithOpt :: Expr ArithSym -> Expr ArithSym
arithOpt = opt arithConfig32

Но если я изменяю экзистенциальный на GADT, он делает:

{-# LANGUAGE GADTs #-}
[...]

-- |Configuration to optimize an expression with symbols of type e.
data OptConfig e = OptConfig {
  ocSymSize :: e -> Int,
  ocGSymIso0 :: GIso e
  -- ^ this should be existential
  }
data GIso e where
  GIso :: (e -> GSym e0,GSym e0 -> e) -> GIso e

-- |Optimize given the configuration
opt :: OptConfig e -> Expr e -> Expr e
opt oc = case ocGSymIso0 oc of
  GIso (fromE,toE) ->
    otherOpt (ocSymSize oc)
    . fmap toE
    . addInts
    . concatStrings
    . fmap fromE


arithConfig32 :: OptConfig ArithSym
arithConfig32 = OptConfig {
  ocSymSize=const 4,
  ocGSymIso0=GIso
    (\case{AInt i -> GInt i;ASym s -> GOther s},
     \case {GInt i -> AInt i;GOther a -> ASym a;_-> error "unreachable"})
  -- XXX: It works now?
  }

[...]

Может кто-нибудь объяснить мне, почему одна проверка типов но не другой

1 Ответ

3 голосов
/ 27 апреля 2020

Последний является экзистенциальным, но первый - нет: вместо этого это универсальное количественное определение.

Обратите внимание на типы конструктора:

-- first definition
OptConfig :: forall e. (e -> Int) -> (forall e0 . (e -> GSym e0,GSym e0 -> e))
          -> OptConfig e
-- second definition
OptConfig :: forall e e0. (e -> Int) -> (e -> GSym e0,GSym e0 -> e)
          -> OptConfig e

Положение forall e0 отличается, изменяя действительное значение квантификатора, с универсального на экзистенциальное.

Вместо этого попробуйте эту запись, если вы не хотите использовать синтаксис GADT (который я бы предпочел):

-- |Configuration to optimize an expression with symbols of type e.
data OptConfig e = forall e0 . OptConfig {
  ocSymSize :: e -> Int,
  ocGSymIso :: (e -> GSym e0,GSym e0 -> e)
  -- ^ this should be existential
  }
...