Haskell类型类的部分实例化

作为某种形式的实验,我试图为 Haskell 中的纸牌游戏定义一个非常通用的逻辑。为了保证最大的抽象级别,我定义了以下类型类:

class (Monad m) => CardGame m where
    data Deck   m :: *
    data Card   m :: *
    data Player m :: *

    game :: m () -- a description of the game's rules 

    draw :: Deck m -> m (Card m)             -- top card of deck is drawn
    pick :: Deck m -> Player m -> m (Card m) -- player selects card from a deck
    -- so on and so forth

这门课对我来说非常令人满意,因为它保留了将使用哪些卡(例如塔罗牌、法国花、德国花等)、玩家是谁等以及游戏应该运行什么样的底层单子的问题进入。

现在假设我想定义类似战争的纸牌游戏。这些游戏是在两个玩家之间进行的,每个玩家收到一副法式纸牌游戏,但是游戏本身有很多变体,所以我无法提前指定确切的规则是什么(即game) 我怎样才能部分特化上面的类型类?编译器对以下情况感到不安:

data Suit   = Heart | Diamond | Spade | Club
data Value  = Number Int | Jack | Queen | King 
data FrenchSuited = Card Suit Value


class (Game m) => War m where
    -- syntax error in the following line:
    data Card m = FrenchSuited -- any War-like game must be played on French-suited card
    deck1   :: Deck m
    deck2   :: Deck m
    player1 :: Player m
    player2 :: Player m

你看到我可以做到这一点的方法吗?

PS:战争类游戏真的可以用任何类型的卡玩,但这只是一个例子。我对如何实现我所追求的类型类的部分专业化类型更感兴趣。

回答

你不能用数据族来做到这一点:Card m必须是一个不同的类型,Card m'用于不同的mm'。这是因为数据族需要是单射的,就像常规类型构造函数一样。

充其量,Card m并且Card m'可以与 同构FrenchSuited

相反,类型族没有这种注入性限制,但正因为如此,它们有时会使类型检查更加微妙,需要用户以某种方式解决一些歧义。话虽如此,您可以按如下方式要求您的类型:

{-# LANGUAGE TypeFamilies, AllowAmbiguousTypes #-}

class (Monad m) => CardGame m where
    type Deck   m :: *
    type Card   m :: *
    type Player m :: *

    game :: m () -- a description of the game's rules 

    draw :: Deck m -> m (Card m)             -- top card of deck is drawn
    pick :: Deck m -> Player m -> m (Card m) -- player selects card from a deck
    -- so on and so forth


data Suit   = Heart | Diamond | Spade | Club
data Value  = Number Int | Jack | Queen | King 
data FrenchSuited = Card Suit Value


class (CardGame m, Card m ~ FrenchSuited) => War m where
    deck1   :: Deck m
    deck2   :: Deck m
    player1 :: Player m
    player2 :: Player m

Card m ~ FrenchSuited子类中的约束力量型的平等。

  • @AhmadB: Data families are more like the converse of a GADT: with a GADT, if you match on the constructors, then you can know the type; whereas with a data family, if you know the type, then you can match on the constructors. Type families are type-level functions, which are often more convenient than the more general type-level relations offered by multi-parameter type classes (with functional dependencies).

以上是Haskell类型类的部分实例化的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>