Haskell:具有幻像变量的数据的异构列表

gsp*_*les 10 haskell existential-type gadt phantom-types

我正在学习存在量化,幻像类型和GADT.如何使用幻像变量创建数据类型的异构列表?例如:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}

data Toy a where
  TBool :: Bool -> Toy Bool
  TInt  :: Int  -> Toy Int

instance Show (Toy a) where
  show (TBool b) = "TBool " ++ show b
  show (TInt  i) = "TInt "  ++ show i

bools :: [Toy Bool]
bools = [TBool False, TBool True]

ints  :: [Toy Int]
ints  = map TInt [0..9]
Run Code Online (Sandbox Code Playgroud)

具有以下功能可以:

isBool :: Toy a -> Bool
isBool (TBool _) = True
isBool (TInt  _) = False

addOne :: Toy Int -> Toy Int
addOne (TInt a) = TInt $ a + 1
Run Code Online (Sandbox Code Playgroud)

但是,我希望能够像这样声明异构列表:

zeros :: [Toy a]
zeros =  [TBool False, TInt 0]
Run Code Online (Sandbox Code Playgroud)

我尝试使用空类型来限制类型a:

class Unify a
instance Unify Bool
instance Unify Int

zeros :: Unify a => [Toy a]
zeros =  [TBool False, TInt 0]
Run Code Online (Sandbox Code Playgroud)

但上述内容无法编译.我能够使用存在量化来获得以下内容:

data T = forall a. (Forget a, Show a) => T a

instance Show T where
  show (T a) = show a

class (Show a) => Forget a
instance Forget (Toy a)
instance Forget T

zeros :: [T]
zeros = [T (TBool False), T (TInt 0)]
Run Code Online (Sandbox Code Playgroud)

不过这样一来,我无法应用是基于特定类型的函数aToy aT,例如addOne上面.

总之,有什么方法可以创建异构列表而不会忘记/丢失幻像变量?

dfe*_*uer 10

Toy类型开始:

data Toy a where
  TBool :: Bool -> Toy Bool
  TInt :: Int -> Toy Int
Run Code Online (Sandbox Code Playgroud)

现在你可以将它包装在一个存在主义中而不会过度泛化类系统:

data WrappedToy where
  Wrap :: Toy a -> WrappedToy
Run Code Online (Sandbox Code Playgroud)

由于包装器只保存Toys,我们可以打开它们并Toy返回:

incIfInt :: WrappedToy -> WrappedToy
incIfInt (Wrap (TInt n)) = Wrap (TInt (n+1))
incIfInt w = w
Run Code Online (Sandbox Code Playgroud)

现在您可以区分列表中的内容:

incIntToys :: [WrappedToy] -> [WrappedToy]
incIntToys = map incIfInt
Run Code Online (Sandbox Code Playgroud)

编辑

正如Cirdec指出的那样,不同的部分可以分开一点:

onInt :: (Toy Int -> WrappedToy) -> WrappedToy -> WrappedToy
onInt f (Wrap t@(TInt _)) = f t
onInt _ w = w

mapInt :: (Int -> Int) -> Toy Int -> Toy Int
mapInt f (TInt x) = TInt (f x)

incIntToys :: [WrappedToy] -> [WrappedToy]
incIntToys = map $ onInt (Wrap . mapInt (+1))
Run Code Online (Sandbox Code Playgroud)

我还应该注意到,到目前为止,没有什么能够证明ToyGADT 是正确的.bheklilr使用普通代数数据类型的简单方法应该可以正常工作.

  • 知道"WrappedToy"中的东西是"玩具"并没有完成任何事情.它可以更一般地写成`data Wrapped f where Wrap :: fa - > Wrapped f`并用作`Wrapped Toy`而不会丢失任何东西. (2认同)

use*_*465 6

几天前有一个非常相似的问题.

在你的情况下,它会

{-# LANGUAGE GADTs, PolyKinds, Rank2Types #-}

data Exists :: (k -> *) -> * where
  This :: p x -> Exists p

type Toys = [Exists Toy]

zeros :: Toys
zeros = [This (TBool False), This (TInt 0)]
Run Code Online (Sandbox Code Playgroud)

消除存在主义很容易:

recEx :: (forall x. p x -> c) -> Exists p -> c
recEx f (This x) = f x
Run Code Online (Sandbox Code Playgroud)

然后,如果您有Toy数据类型的recursor

recToy :: (Toy Bool -> c) -> (Toy Int -> c) -> Toy a -> c
recToy f g x@(TBool _) = f x
recToy f g x@(TInt  _) = g x
Run Code Online (Sandbox Code Playgroud)

你可以映射一个包裹toy:

mapToyEx :: (Toy Bool -> p x) -> (Toy Int -> p y) -> Exists Toy -> Exists p
mapToyEx f g = recEx (recToy (This . f) (This . g))
Run Code Online (Sandbox Code Playgroud)

例如

non_zeros :: Toys
non_zeros = map (mapToyEx (const (TBool True)) addOne) zeros
Run Code Online (Sandbox Code Playgroud)

这种方法与@ dfeuer的答案类似,但它不那么特别.


Cir*_*dec 5

由其元素类型列表索引的普通异构列表是

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}

data HList l where
    HNil :: HList '[]
    HCons :: a -> HList l -> HList (a ': l)
Run Code Online (Sandbox Code Playgroud)

我们可以修改它来保存一些值f :: * -> *.

data HList1 f l where
    HNil1 :: HList1 f '[]
    HCons1 :: f a -> HList1 f l -> HList1 f (a ': l)
Run Code Online (Sandbox Code Playgroud)

您可以使用它来编写zeros而不会忘记类型变量.

zeros :: HList1 Toy [Bool, Int]  
zeros = HCons1 (TBool False) $ HCons1 (TInt 0) $ HNil1
Run Code Online (Sandbox Code Playgroud)