gpt4 book ai didi

haskell - 非法实例声明/重叠实例

转载 作者:行者123 更新时间:2023-12-02 05:42:59 25 4
gpt4 key购买 nike

给定类 X 和 Y,创建彼此类的实例的最惯用方法是什么?例如-

instance (X a) => Y a where ...
instance (Y a) => X a where ...

我想避免扩展。另外,我知道这可能会导致一些讨厌的无限递归,所以我愿意采用一种完全不同的方法来完成同样的事情并保持相对干燥。下面给出了一些关于我遇到的确切问题的上下文-

data Dealer = Dealer Hand
data Player = Player Hand Cash

class HasPoints a where
getPoints :: a -> Int

class (HasPoints a) => CardPlayer a where
getHand :: a -> Hand

viewHand :: a -> TurnIsComplete -> Hand

hasBlackjack :: a -> Bool
hasBlackjack player = getPoints player == 21 &&
(length . getCards . getHand) player == 2

busts :: a -> Bool
busts player = getPoints player > 21

我想这样做-

instance (CardPlayer a) => HasPoints a where
getPoints = getPoints . getHand

但看来我必须这样做-

instance HasPoints Dealer where
getPoints = getPoints . getHand

instance HasPoints Player where
getPoints = getPoints . getHand

编辑

似乎我最喜欢的方法是保留 HasPoints 类型类并将 CardPlayer 实现为 data

data CardPlayer = Dealer Hand | Player Hand Cash

instance HasPoints CardPlayer where
getPoints = getPoints . getHand

getCash :: CardPlayer -> Maybe Cash
getHand :: CardPlayer -> Hand
viewHand :: CardPlayer -> TurnIsComplete -> Hand
hasBlackjack :: CardPlayer -> Bool
busts :: CardPlayer -> Bool

-- I wanted HasPoints to be polymorphic
-- so it could handle Card, Hand, and CardPlayer

instance HasPoints Hand where
getPoints Hand { getCards = [] } = 0

getPoints hand = if base > 21 && numAces > 0
then maximum $ filter (<=21) possibleScores
else base
where base = sum $ map getPoints $ getCards hand
numAces = length $ filter ((Ace==) . rank) $ getCards hand
possibleScores = map ((base-) . (*10)) [1..numAces]

instance HasPoints Card where
-- You get the point

最佳答案

Given class X and Y, what's the most idiomatic approach to creating instances of each other's class?

根据您的示例代码,惯用的方法是在类型类没有做任何有用的事情时首先不使用它们。考虑类函数的类型:

class HasPoints a where
getPoints :: a -> Int

class (HasPoints a) => CardPlayer a where
getHand :: a -> Hand
viewHand :: a -> TurnIsComplete -> Hand
hasBlackjack :: a -> Bool
busts :: a -> Bool

他们有什么共同点?它们都只采用类参数类型的一个值作为它们的第一个参数,因此给定这样一个值,我们可以将每个函数应用于它并获得所有相同的信息,而无需担心类约束。

因此,如果您想要一个不错的、惯用的 DRY 方法,请考虑以下内容:

data CardPlayer a = CardPlayer
{ playerPoints :: Int
, hand :: Hand
, viewHand :: TurnIsComplete -> Hand
, hasBlackjack :: Bool
, busts :: Bool
, player :: a
}

data Dealer = Dealer
data Player = Player Cash

在此版本中,类型CardPlayer PlayerCardPlayer Dealer 等同于PlayerDealer 类型你有过。这里的 player 记录字段用于获取特定于播放器类型的数据,并且在您的函数中具有类约束的多态函数可以简单地对 CardPlayer a 类型的值进行操作

尽管将 hasBlackjackbusts 设为常规函数(就像您的默认实现)可能更有意义,除非您真的需要对免疫的玩家建模二十一点的标准规则。

从这个版本开始,如果你有非常不同的类型应该是它的实例,你现在可以单独定义一个 HasPoints 类,尽管我怀疑它的实用性,或者你可以申请同样的转换得到另一层:

data HasPoints a = HasPoints
{ points :: Int
, pointOwner :: a
}

但是,当您像这样嵌套特化时,这种方法很快就会变得笨拙。

我建议完全放弃 HasPoints。它只有一个函数,它只是提取一个 Int,因此任何处理 HasPoints 实例的代码通常也可以只使用 Int 并且是完成它。

关于haskell - 非法实例声明/重叠实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16161384/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com