哇,您已经有了一个类型类、两个实例和一个状态单子,而您甚至还没有确定 MVar 的类型!事情变得复杂了。
我担心你可能会落入 Haskell-as-Java 陷阱,在这个陷阱中,你已经在脑海中想出了一个面向对象的解决方案,而你现在正试图将它直接翻译成 Haskell,思考你的舞者作为有状态的对象,共享方法包含在“类”等中。
我会建议一种不同的方法。舞者不是“东西”;他们是任务。将它们实现为简单的函数,并使用参数传递和递归来代替“状态”,这对于惯用的 Haskell 来说是典型的。
剧透如下,但这里有一个简单的方法来定义一个“follower”,它有一个id,通过一对请求/响应 MVar 响应请求,并使用以下方法维护一个舞卡递归核心循环。请注意,Follower 数据类型不应该是“追随者对象”(例如,它没有舞蹈卡);这只是记录来自follower 的返回值的一种便捷方式,它用作识别和与追随者任务通信的“句柄”:
type LeaderId = Int
type FollowerId = Int
type Dance = Int
-- |A dance card for a follower with a list of dance/leader pairs.
data Card = Card { getCard :: [(Dance, LeaderId)] } deriving (Show)
emptyCard = Card []
-- |Follower handle giving its id and request/response MVars
data Follower =
Follower { followerId :: FollowerId
, request :: MVar (Dance, LeaderId)
, response :: MVar Bool
}
-- |Create a new follower task with given id.
follower :: FollowerId -> IO Follower
follower followerId_ = do
req <- newEmptyMVar
res <- newEmptyMVar
let loop (Card xs) = do
-- get next request
(dance, leaderId_) <- takeMVar req
case lookup dance xs of
-- if dance is free and we haven't danced too often w/ this leader
Nothing | length (filter ((==leaderId_) . snd) xs) < 2
-- then say yes and update dance card
-> do putMVar res True
loop (Card $ (dance, leaderId_) : xs)
-- otherwise, refuse
_ -> do putMVar res False
loop (Card xs)
forkIO $ loop emptyCard
return $ Follower followerId_ req res
您可以通过邀请他们跳舞来创建和测试几个追随者:
> f1 <- follower 1 -- follower #1
> f2 <- follower 2 -- follower #2
> putMVar (request f1) (1, 10) -- dance #1 w/ leader #10
> takeMVar (response f1)
True -- hooray!
> putMVar (request f1) (1, 14) -- dance #1 w/ leader #14
> takeMVar (response f1)
False -- wah! dance is taken
> putMVar (request f2) (1, 14) -- try different follower
> takeMVar (response f2)
True -- hooray!
>
请注意,这些特定的追随者既不能被查询他们的舞蹈卡,也不能被告知退出他们的无限循环。对于这个应用程序你不需要它(我们只需要领导者的舞卡,而不关心当我们得到答案时是否有一堆卡住的轻量级线程),但你总是可以添加几个 MVar,如果你做到了。
同样,您应该能够将领导者实现为具有简单递归核心循环的函数。请注意,如果领导者试图按顺序填写其舞蹈卡,它实际上不需要跟踪舞蹈卡——最终的舞蹈卡(和“核心循环”)只是一个@987654326 @over 试图填补 1 到 8 的舞蹈位置。
您如何为领导者提供请求追随者跳舞的能力?好吧,首先创建完整的追随者集,并将追随者句柄列表 ([Follower]) 作为参数传递给 leader 创建函数。你如何从领导那里拿回舞蹈卡? leader 函数应返回卡片的 MVar,main 函数可以通过 mapM takeMVar leadersDanceCards 获取舞蹈卡片的完整列表。