使用GHC.Generics或Data.Data键入Families

jbe*_*man 11 generics haskell ghc type-families

这是关系到我的模块一个问题在这里,并简化了一下.它也与之前的问题有关,我在这个问题中过度简化了我的问题并没有得到我想要的答案.我希望这不是太具体,如果你能想到更好的话,请更改标题.

背景

我的模块使用并发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...
Run Code Online (Sandbox Code Playgroud)

上面的代码允许我们做这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

我们有一个名为a的递归动作Behavior,我们可以对从通道的"读取"端拉出的消息运行:

newtype Behavior a = Behavior (a -> IO (Behavior a))
runBehaviorOn :: Behavior a -> Messages a -> IO ()  -- NOT EXPORTED
Run Code Online (Sandbox Code Playgroud)

这将允许我们Behavior (Int,Int)在或者运行一个msgsA或者msgsB,在第二种情况下Int,它接收的元组中的两个s实际上都通过单独的Mailboxes运行.

这对于暴露spawn功能中的用户而言都是捆绑在一起的

spawn :: (Sources s) => Behavior (Joined s) -> IO s
Run Code Online (Sandbox Code Playgroud)

...调用newJoinedChanrunBehaviorOn返回输入Sources.

我想做什么

我希望用户能够创建Behavior任意产品类型(不仅仅是元组),所以我们可以Behavior (Pair Int Int)Messages上面的示例中运行.我想GHC.Generics在仍然具有多态性的情况下执行此操作Sources,但无法使其工作.

spawn :: (Sources s, Generic (Joined s), Rep (Joined s) ~ ??) => Behavior (Joined s) -> IO s
Run Code Online (Sandbox Code Playgroud)

被在API中实际上露出的上述示例的部分是fst所述的newJoinedChan动作,和BehaviorS,SO可接受的解决方案可以修改一个或所有的runBehaviorOnsndnewJoinedChan.

我也将扩展API以支持总和(尚未实现),Behavior (Either a b)因此我希望GHC.Generics能为我工作.

问题

  1. 有没有办法可以扩展上面的API以支持任意Generic a=> Behavior a

  2. 如果不使用GHC的泛型,是否有其他方法可以获得我想要的API,同时最小化最终用户的痛苦(即他们只需要在其类型中添加派生子句)?例如Data.Data

小智 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))
Run Code Online (Sandbox Code Playgroud)