Haskell:具有幻像变量的数据的异构列表 [英] Haskell: Heterogeneous list for data with phantom variable

查看:115
本文介绍了Haskell:具有幻像变量的数据的异构列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此刻,我正在学习关于存在性量化,幻像类型和GADT的知识.如何创建带有幻像变量的数据类型的异构列表?例如:

I'm learning about existential quantification, phantom types, and GADTs at the moment. How do I go about creating a heterogeneous list of a data type with a phantom variable? For example:

{-# 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]

具有如下功能可以:

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

addOne :: Toy Int -> Toy Int
addOne (TInt a) = TInt $ a + 1

但是,我希望能够这样声明一个异类列表:

However, I would like to be able to declare a heterogeneous list like so:

zeros :: [Toy a]
zeros =  [TBool False, TInt 0]

我尝试使用空类型类通过以下方式限制a上的类型:

I tried using an empty type class to restrict the type on a by:

class Unify a
instance Unify Bool
instance Unify Int

zeros :: Unify a => [Toy a]
zeros =  [TBool False, TInt 0]

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

But the above would fail to compile. I was able to use existential quantification to do get the following:

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)]

但是这样,我不能将基于Toy a中特定类型的a的函数应用于T. addOne以上.

But this way, I cannot apply a function that was based on the specific type of a in Toy a to T e.g. addOne above.

总而言之,在不忘记/丢失幻象变量的情况下,我可以通过哪些方式创建异构列表?

In conclusion, what are some ways I can create a heterogeneous list without forgetting/losing the phantom variable?

推荐答案

Toy类型开头:

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

现在,您可以将其包装在存在的中,而不会因类系统而过度概括:

Now you can wrap it up in an existential without over-generalizing with the class system:

data WrappedToy where
  Wrap :: Toy a -> WrappedToy

由于包装器仅容纳Toy个,因此我们可以解开它们并收回Toy个:

Since the wrapper only holds Toys, we can unwrap them and get Toys back:

incIfInt :: WrappedToy -> WrappedToy
incIfInt (Wrap (TInt n)) = Wrap (TInt (n+1))
incIfInt w = w

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

And now you can distinguish things within the list:

incIntToys :: [WrappedToy] -> [WrappedToy]
incIntToys = map incIfInt

编辑

正如Cirdec所指出的那样,可以将一些不同的部分分开来弄弄:

Edit

As Cirdec points out, the different pieces can be teased apart a bit:

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))

我还应该指出,到目前为止,没有什么可以真正证明Toy GADT的合理性. bheklilr使用简单的代数数据类型的更简单方法应该可以正常工作.

I should also note that nothing here so far really justifies the Toy GADT. bheklilr's simpler approach of using a plain algebraic datatype should work just fine.

这篇关于Haskell:具有幻像变量的数据的异构列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆