首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >确保异类列表包含一个类型类

确保异类列表包含一个类型类
EN

Stack Overflow用户
提问于 2018-06-02 04:22:22
回答 2查看 84关注 0票数 2

我已经编写了自己的异构列表实现(我第一次读到异构列表here时,我的实现与他们的非常相似)

代码语言:javascript
复制
{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}

data HList a where
  (:>) :: a -> HList b -> HList (a ': b)
  Nil  :: HList '[]
infixr 6 :>

这很棒;然而,一旦我发现自己使用了这些异构列表,我发现自己经常想要表达这样的想法,即每个类型都属于一个特定的类型类。我天真的第一个解决方案是为每个新类型类完全重写HList数据类型。这是针对Eq的(仅举个例子;它不是我唯一关心的类型类):

代码语言:javascript
复制
{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}

data EqHList a where
  (:>) :: (Eq a) => a -> EqHList b -> EqHList (a ': b)
  Nil :: EqHList '[]
infixr 6 :>

这有一大堆问题。首先,每当我想要一个新的类型类时,我都需要重写它。此外,在我的旧的异构列表上工作的函数不能在新的列表上工作。

我的下一个解决方案是使用空类型类。

代码语言:javascript
复制
{-# LANGUAGE GADTs, DataKinds, TypeOperators, FlexibleInstances, FlexibleContexts #-}

data HList a where
  (:>) :: a -> HList b -> HList (a ': b)
  Nil :: HList '[]
infixr 6 :>

class Eqed a

instance Eqed (HList '[])
instance (Eq a, Eqed (HList b)) => Eqed (HList (a ': b))

这里,Eqed的一个实例是一个HList,它的所有元素都是Eq的实例。这肯定比上一个解决方案更好,但是我仍然觉得它缺乏。每当我想要谈论不同类型的类时,我仍然需要复制粘贴一堆代码。我觉得这类问题可以通过更多的类型级编程来解决。

有没有更好的方法?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-02 04:33:14

您可以通过参数化来泛化Eqed (需要一些扩展,GHC的错误消息会告诉您):

代码语言:javascript
复制
import Data.Kind (Constraint)

class CMap (c :: * -> Constraint) (xs :: [*])
instance CMap c '[]
instance (c x, CMap c xs) => CMap c (x ': xs)

然而,这并不是很好的表现,因为CMap c (x ': xs)并不意味着c x,它只是反过来。一种方法可能是添加一个可以使用这些单独约束的方法,但这似乎很麻烦。另一种是使用类型族:

代码语言:javascript
复制
type family CMap (c :: * -> Constraint) (xs :: [*]) :: Constraint where
  CMap c '[] = ()
  CMap c (x ': xs) = (c x, CMap c xs)
票数 7
EN

Stack Overflow用户

发布于 2018-06-02 04:33:48

您可以使用ConstraintKinds将约束设置为HList的参数

代码语言:javascript
复制
data HList c a where
  (:>) :: (c a) => a -> HList c b -> HList c (a ': b)
  Nil :: HList c '[]

exampleList :: HList Eq '[Int, String, Double]
exampleList
  = (1 :: Int)
  :> ("two" :: String)
  :> (3.0 :: Double)
  :> Nil

或者使用ExistentialTypes/GADTs和常规列表,如果您只需要类型类,并且不介意丢失有关列表中哪些具体类型的静态类型信息:

代码语言:javascript
复制
data SomeEq = forall a. Eq a => SomeEq a

data SomeEq where
  SomeEq :: Eq a => a -> SomeEq

exampleList :: [SomeEq]
exampleList =
  [ SomeEq (1 :: Int)
  , SomeEq ("two" :: String)
  , SomeEq (3.0 :: Double)
  ]

但是,对于Eq,这并不是特别有用,除非您还传播有关类型相等的信息,以便您有一些有用的东西来比较抽象包装值。实际上,您唯一能做的就是将包装的值与其自身进行比较。

根据您的特定应用程序,可能会有更好/更简单的选项。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50650543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档