如何在 Haskell 中的泛型 "super type" 下拥有多个类型?
How to have multiple types under a generic "super type" in Haskell?
[不要管这个问题 - 根据反馈,我意识到它措辞不佳且过于模糊。我不删除它,因为答案可以为其他人服务。谢谢!]
我正在寻找一种类型结构,它允许我将两个相似的有序数据类型放在一个通用类型下。
例如,假设我有以下有序类型:
data Type1 = Type1Level1 | Type1Level2 | Type1Level3 | Type1Level4 deriving (Eq, Ord, Show, Read, Bounded, Enum)
data Type2 = Type2Level1 | Type2Level2 | Type2Level3 deriving (Eq, Ord, Show, Read, Bounded, Enum)
本着精神,我想要一个通用的总体类型,例如:
data GenType = Type1 | Type2
但这当然行不通。
我碰巧拥有与其中一种类型相关的实体:
data Entity = Entity {
entityId :: String,
entityType :: -- here I want one of the TypeXLevelY (anything "under GenType")
} deriving (Eq, Ord, Read, Show)
但我还希望这些实体具有特定字段,具体取决于它们是 type1 还是 type2(这是否意味着我必须制作 2 种不同的“实体”数据类型?)。例如,type1 的实体应该有一个字段“input”,但 type2 的实体没有。
此外,type1 和 type2 应该能够共享顺序和几个共同的函数,这样这样的事情才有意义:
testSuccType :: GenType -> GenType -> Bool
testSuccType type1 type2 = (type2 == succ type1)
看起来我在这里需要一个 class 但是 (1) 我不确定情况是否如此以及 (2) 当我尝试为实体使用限定类型时,它不喜欢它(显然不再支持这种资格):
data GenType a => Entity = Entity {
entityId :: String,
entityType :: a
} deriving (Eq, Ord, Read, Show)
我希望这已经足够清楚了。
非常感谢您的帮助。
我想我没有完全理解这个问题,但也许我可以给出一些提示。
您要查找的数据类型定义可能是:
data GenType = GT1 Type1 | GT2 Type2
或者,有些惯用,您可以将构造函数名称与它们的(唯一)字段的类型双关:
data GenType = Type1 Type1 | Type2 Type2
然后你可以像这样实现你的共享函数:
getNextLevelGenType (GT1 t1) = GT1 (getNextLevelType1 t1)
getNextLevelGenType (GT2 t2) = GT2 (getNextLevelType2 t2)
-- OR
getNextLevelGenType (Type1 t1) = Type1 (getNextLevelType1 t1)
getNextLevelGenType (Type2 t2) = Type2 (getNextLevelType2 t2)
有时通过类型 class 共享方法名称是有意义的,但这是否有意义在很大程度上取决于问题中未共享的细节。如果确实有意义,它可能看起来像这样:
class Leveled a where getNextLevel :: a -> a
instance Leveled Type1 where getNextLevel = succ
instance Leveled Type2 where getNextLevel = succ
instance Leveled GenType where
getNextLevel (Type1 t1) = Type1 (getNextLevel t1)
getNextLevel (Type2 t2) = Type2 (getNextLevel t2)
如果你走那条路,有一个通用的、参数化的总和类型可能值得考虑作为 GenType
的替代,名为 Either
。避免考虑它的主要原因是 GenType
的构造函数名称是否可以作为其含义的人类可读文档。 (上面的例子中,名称只表示字段类型,不是很好的人类可读文档。)第二个原因是如果你真的想要三种或更多类型的总和,因为嵌套 Either
s变得笨拙。
instance (Leveled a, Leveled b) => Leveled (Either a b) where
getNextLevel (Left a) = Left (getNextLevel a)
getNextLevel (Right b) = Right (getNextLevel b)
-- OR, with Data.Bifunctor imported,
getNextLevel = bimap getNextLevel getNextLevel
[不要管这个问题 - 根据反馈,我意识到它措辞不佳且过于模糊。我不删除它,因为答案可以为其他人服务。谢谢!]
我正在寻找一种类型结构,它允许我将两个相似的有序数据类型放在一个通用类型下。
例如,假设我有以下有序类型:
data Type1 = Type1Level1 | Type1Level2 | Type1Level3 | Type1Level4 deriving (Eq, Ord, Show, Read, Bounded, Enum)
data Type2 = Type2Level1 | Type2Level2 | Type2Level3 deriving (Eq, Ord, Show, Read, Bounded, Enum)
本着精神,我想要一个通用的总体类型,例如:
data GenType = Type1 | Type2
但这当然行不通。
我碰巧拥有与其中一种类型相关的实体:
data Entity = Entity {
entityId :: String,
entityType :: -- here I want one of the TypeXLevelY (anything "under GenType")
} deriving (Eq, Ord, Read, Show)
但我还希望这些实体具有特定字段,具体取决于它们是 type1 还是 type2(这是否意味着我必须制作 2 种不同的“实体”数据类型?)。例如,type1 的实体应该有一个字段“input”,但 type2 的实体没有。
此外,type1 和 type2 应该能够共享顺序和几个共同的函数,这样这样的事情才有意义:
testSuccType :: GenType -> GenType -> Bool
testSuccType type1 type2 = (type2 == succ type1)
看起来我在这里需要一个 class 但是 (1) 我不确定情况是否如此以及 (2) 当我尝试为实体使用限定类型时,它不喜欢它(显然不再支持这种资格):
data GenType a => Entity = Entity {
entityId :: String,
entityType :: a
} deriving (Eq, Ord, Read, Show)
我希望这已经足够清楚了。 非常感谢您的帮助。
我想我没有完全理解这个问题,但也许我可以给出一些提示。
您要查找的数据类型定义可能是:
data GenType = GT1 Type1 | GT2 Type2
或者,有些惯用,您可以将构造函数名称与它们的(唯一)字段的类型双关:
data GenType = Type1 Type1 | Type2 Type2
然后你可以像这样实现你的共享函数:
getNextLevelGenType (GT1 t1) = GT1 (getNextLevelType1 t1)
getNextLevelGenType (GT2 t2) = GT2 (getNextLevelType2 t2)
-- OR
getNextLevelGenType (Type1 t1) = Type1 (getNextLevelType1 t1)
getNextLevelGenType (Type2 t2) = Type2 (getNextLevelType2 t2)
有时通过类型 class 共享方法名称是有意义的,但这是否有意义在很大程度上取决于问题中未共享的细节。如果确实有意义,它可能看起来像这样:
class Leveled a where getNextLevel :: a -> a
instance Leveled Type1 where getNextLevel = succ
instance Leveled Type2 where getNextLevel = succ
instance Leveled GenType where
getNextLevel (Type1 t1) = Type1 (getNextLevel t1)
getNextLevel (Type2 t2) = Type2 (getNextLevel t2)
如果你走那条路,有一个通用的、参数化的总和类型可能值得考虑作为 GenType
的替代,名为 Either
。避免考虑它的主要原因是 GenType
的构造函数名称是否可以作为其含义的人类可读文档。 (上面的例子中,名称只表示字段类型,不是很好的人类可读文档。)第二个原因是如果你真的想要三种或更多类型的总和,因为嵌套 Either
s变得笨拙。
instance (Leveled a, Leveled b) => Leveled (Either a b) where
getNextLevel (Left a) = Left (getNextLevel a)
getNextLevel (Right b) = Right (getNextLevel b)
-- OR, with Data.Bifunctor imported,
getNextLevel = bimap getNextLevel getNextLevel