类型检查器可以帮助我吗? 有类型的家庭,也许?
所以我现在正在写这个小小的足球比赛,有一件事从一开始就让我感到困惑。 游戏遵循Yampa Arcade模式,所以游戏中的“对象”有一个总和类型:
data ObjState = Ball Id Pos Velo
| Player Id Team Number Pos Velo
| Game Id Score
对象对消息作出反应,所以还有另一种总和类型:
data Msg = BallMsg BM
| PlayerMsg PM
| GameMsg GM
data BM = Gained | Lost
data PM = GoTo Position | Shoot
data GM = GoalScored | BallOutOfBounds
Yampa框架依赖于所谓的信号功能。 在我们的例子中,有球,球员和游戏行为的信号功能。 粗略简化:
ballObj, playerObj, gameObj :: (Time -> (GameInput, [Msg]))
-> (Time -> (ObjState, [(Id, Msg)]))
因此,例如ballObj需要一个函数,该函数可以在任何给定时间产生GameInput(击键,游戏状态......)以及专门用于球的消息列表,并返回一个函数,该函数可以产生球的状态,并将消息传递给其他对象(球,游戏,球员)在任何给定的时间。 在Yampa,类型签名实际上看起来更好一些:
ballObj, playerObj, gameObj :: SF (GameInput, [Msg]) (ObjState, [(Id, Msg)])
这种统一类型的签名对于Yampa框架是非常重要的:(再次,非常粗暴地简化了),它从11 + 11(玩家)+ 1(球)+ 1(游戏)信号函数列表中建立了一个大信号函数, (通过dpSwitch),然后运行(通过reactimate)。
所以,现在,什么使我感到困惑:将BallMsg发送给球或者将PlayerMsg发送给玩家才有意义。 如果有人将例如GameMsg发送给Ball,程序将会崩溃。 没有办法让类型检查器避免这种情况吗? 我最近在类型系列中阅读了这个不错的Pokemon帖子,似乎有些类比。 所以也许这可能是一个起点:
class Receiver a where
Msg a :: *
putAddress :: Msg a -> a -> Msg a
data BallObj = ...
data GameObj = ...
data PlayerObj = ...
instance Receiver BallObj where
Msg BallObj = Gained | Lost
(...)
现在,SF功能可能如下所示:
forall b . (Receiver a, Receiver b) => SF (GameInput, [Msg a]) (a, [(b, Msg b)])
这会让我到哪里?
从乍一看直观你的设计的一个主要问题突出:你在单一类型下统一了完全不同的实体Ball
, Player
和Game
。 如果你需要这些实体的联合类型,那么通过使它们成为不同的类型来使用与消息相同的方式,即:
data AnyObject = AnyObjectBall Ball
| AnyObjectPlayer Player
| AnyObjectGame Game
这样你就可以表达具体的功能( Ball -> BallMsg -> ...
)和一般的功能( AnyObject -> AnyMsg -> ...
)。
但如果我正确理解你的问题,我想我有一个解决方案,你不需要联盟类型:
class Signal object message where
signal :: SF (GameInput, [message]) (object, [(Id, message)])
data Ball = Ball Id Pos Velo
data BallMsg = BallMsgGained | BallMsgLost
instance Signal Ball BallMsg where
-- ...
-- so on for Player and Game
浏览yampa街机纸,似乎你有一个从他们的例子中绘制的route
功能。
我的建议是你改变route
所以它不需要一个单一的对象列表,而是一个单一的游戏对象,一个单一的球对象和一系列球员对象。 然后有
data BallMsg = ...
data PlayerMsg = ...
data GameMsg = ...
data AnyMsg = ABallMsg BallMsg
| APlayerMsg PlayerMsg
| AGameMsg GameMsg
现在route
工作在一个统一的AnyMsg
但是根据它们的内容将它们分派到正确的目的地。
上一篇: Can the type checker help me out here? With type families, maybe?