这是一个与我的模块here相关的问题,并且有点简化。它也与this之前的问题有关,我在其中过度简化了我的问题并没有得到我想要的答案。我希望这不是太具体,如果你能想到更好的话,请更改标题。
我的模块使用并发chan,分为读取侧和写入侧。我使用一个带有关联类型同义词的特殊类来支持多态通道“连接”:
{-# LANGUAGE TypeFamilies #-}
class Sources s where
type Joined s
newJoinedChan :: IO (s, Messages (Joined s)) -- NOT EXPORTED
--output and input sides of channel:
data Messages a -- NOT EXPORTED
data Mailbox a
instance Sources (Mailbox a) where
type Joined (Mailbox a) = a
newJoinedChan = undefined
instance (Sources a, Sources b)=> Sources (a,b) where
type Joined (a,b) = (Joined a, Joined b)
newJoinedChan = undefined
-- and so on for tuples of 3,4,5...
上面的代码允许我们做这样的事情:
example = do
(mb , msgsA) <- newJoinedChan
((mb1, mb2), msgsB) <- newJoinedChan
--say that: msgsA, msgsB :: Messages (Int,Int)
--and: mb :: Mailbox (Int,Int)
-- mb1,mb2 :: Mailbox Int
我们有一个名为Behavior
的递归操作,我们可以对从频道的“读取”端拉出的消息进行操作:
newtype Behavior a = Behavior (a -> IO (Behavior a))
runBehaviorOn :: Behavior a -> Messages a -> IO () -- NOT EXPORTED
这样我们就可以在Behavior (Int,Int)
或msgsA
上运行msgsB
,在第二种情况下,它收到的元组中的Int
个实际上都来了单独Mailbox
es。
对于暴露的spawn
函数
spawn :: (Sources s) => Behavior (Joined s) -> IO s
...调用newJoinedChan
和runBehaviorOn
,并返回输入Sources
。
我希望用户能够创建Behavior
任意产品类型(而不仅仅是元组),因此我们可以在上面的示例Behavior (Pair Int Int)
上运行Messages
。我希望GHC.Generics
使用Sources
同时使用多态spawn :: (Sources s, Generic (Joined s), Rep (Joined s) ~ ??) => Behavior (Joined s) -> IO s
,但无法使其正常工作。
fst
API中实际公开的上述示例部分是newJoinedChan
操作的Behavior
和runBehaviorOn
s,因此可接受的解决方案可以修改其中一个或全部snd
或newJoinedChan
的{{1}}。
我还将上面的API扩展到支持总和(尚未实现),如Behavior (Either a b)
,所以我希望GHC.Generics能为我工作。
我是否有办法扩展上述API以支持任意Generic a=> Behavior a
?
如果不使用GHC的Generics,还有其他方法可以获得我想要的API,同时最小化最终用户的痛苦(即他们只需要在其类型中添加派生子句)吗?例如与Data.Data
?
答案 0 :(得分:4)
也许是这样的?
{-# LANGUAGE TypeFamilies, DeriveGeneric, DefaultSignatures, TypeOperators, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
import Control.Arrow
import GHC.Generics
class Sources s where
type Joined s
newJoinedChan :: IO (s, Messages (Joined s)) -- NOT EXPORTED
default newJoinedChan :: (Generic s, SourcesG (Rep s)) => IO (s, Messages (JoinedG (Rep s)))
newJoinedChan = fmap (first to) newJoinedChanG
class SourcesG g where
type JoinedG g
newJoinedChanG :: IO (g a, Messages (JoinedG g))
--output and input sides of channel:
data Messages a -- NOT EXPORTED
data Mailbox a
instance Sources (Mailbox a) where
type Joined (Mailbox a) = a
newJoinedChan = undefined
instance (Sources a, Sources b)=> Sources (a,b) where
type Joined (a,b) = (Joined a, Joined b)
newJoinedChan = undefined
instance (SourcesG a, SourcesG b) => SourcesG (a :*: b) where
type JoinedG (a :*: b) = (JoinedG a, JoinedG b)
newJoinedChanG = undefined
instance (SourcesG a, Datatype c) => SourcesG (M1 D c a) where
type JoinedG (M1 D c a) = JoinedG a
newJoinedChanG = fmap (first M1) newJoinedChanG
instance (SourcesG a, Constructor c) => SourcesG (M1 C c a) where
type JoinedG (M1 C c a) = JoinedG a
newJoinedChanG = fmap (first M1) newJoinedChanG
instance (SourcesG a, Selector c) => SourcesG (M1 S c a) where
type JoinedG (M1 S c a) = JoinedG a
newJoinedChanG = fmap (first M1) newJoinedChanG
instance Sources s => SourcesG (K1 i s) where
type JoinedG (K1 i s) = Joined s
newJoinedChanG = fmap (first K1) newJoinedChan
newtype Behavior a = Behavior (a -> IO (Behavior a))
runBehaviorOn :: Behavior a -> Messages a -> IO ()
runBehaviorOn = undefined
spawn :: (Sources s) => Behavior (Joined s) -> IO s
spawn = undefined
data Pair a b = Pair a b deriving (Generic)
instance (Sources a, Sources b) => Sources (Pair a b) where
type Joined (Pair a b) = JoinedG (Rep (Pair a b))