我最近开始了一个小爱好项目,在那里我尝试实施技巧卡片游戏Skat(3名玩家).为了能够让不同类型的玩家(如AI,网络和本地)一起玩,我使用类型类设计了一个界面Player:
class Monad m => Player p m | p -> m where
playerMessage :: Message answer -> p -> m (Either Error answer,p)
Run Code Online (Sandbox Code Playgroud)
我用a StateT来结束这三个玩家:
type PST a b c m x = StateT (Players a b c) m x
Run Code Online (Sandbox Code Playgroud)
但是现在,我必须在每个类型签名中写一大堆上下文:
dealCards :: (Player a m, Player b m, Player c m, RandomGen g)
=> g -> PST a b c m (SomeOtherState,g)
Run Code Online (Sandbox Code Playgroud)
我怎样才能避免一次又一次地写这个大背景?
Hei*_*mus 11
您可以从播放器类中观察到的唯一功能是类型的功能
playerMessage' :: Message answer -> m (Either Error answer, p)
Run Code Online (Sandbox Code Playgroud)
因此,您可以完全消除该类并使用普通数据类型
data Player m = Player { playerMessage'
:: Message answer -> m (Either Error answer, Player m) }
Run Code Online (Sandbox Code Playgroud)
这基本上是我以前的答案.
另一种解决方案是使用GADT将上下文移动到数据类型中.
data PST a b c m x where
PST :: (Player a m, Player b m, Player c m)
=> StateT (Players a b c) m x -> PST a b c m x
Run Code Online (Sandbox Code Playgroud)
换句话说,约束成为数据类型的一部分.
在我看来,最好的解决方案是废弃整个产品,并根据我的操作包中的TicTacToe示例重新设计它.这种设计允许您在一个专门的monad中编写每个玩家(人类,AI,重放......),然后将所有内容注入一个通用的解释器.
更新:
当我尝试实施时,dealCards我意识到我的解决方案通过让玩家可以互换来降低类型安全性.通过这种方式,您可以轻松使用一个玩家而不是另一个玩家.
如果你不介意使用ExistentialQuantification,我认为它可以(而且应该?)在这里使用.毕竟,dealCards函数不应该关心和了解a,b以及c,对不对?
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
import Control.Monad.State
import System.Random
type Message answer = answer
type Error = String
class Monad m => Player p m | p -> m where
playerMessage :: Message answer -> p -> m (Either Error answer,p)
data SomePlayer m = forall p. Player p m => SomePlayer p
data Players m = Players (SomePlayer m) (SomePlayer m) (SomePlayer m)
type PST m x = StateT (Players m) m x
dealCards :: (RandomGen g, Monad m) => g -> PST m x
dealCards = undefined
Run Code Online (Sandbox Code Playgroud)
我认为应该可以以Monad类似的方式消除约束.
实际上,在这样的情况下,我觉得类型类被过度使用.也许这是一个Haskell新手在我说话,但我会写这个:
data Player m = Player { playerMessage :: Message answer -> m (Either Error answer, Player m) }
Run Code Online (Sandbox Code Playgroud)