Это вопрос, связанный с моим модулем здесь и немного упрощен. Это также связано с этим предыдущим вопросом, в котором я упростил свою проблему и не получил ответа, который искал. Я надеюсь, что это не слишком конкретно, и, пожалуйста, измените заголовок, если вы можете придумать его лучше.
Задний план
В моем модуле используется параллельный канал, разделенный на сторону чтения и сторону записи. Я использую специальный класс с синонимом связанного типа для поддержки полиморфных «соединений» каналов:
{-# 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
Части приведенного выше примера, которые фактически представлены в API, — это fst
действия newJoinedChan
и Behavior
s, поэтому приемлемое решение может изменить одно или все runBehaviorOn
или snd
действия newJoinedChan
.
Я также буду расширять приведенный выше API для поддержки сумм (еще не реализованных), таких как Behavior (Either a b)
, поэтому я надеялся, что GHC.Generics мне подойдет.
Вопросы
Есть ли способ расширить приведенный выше API для поддержки произвольного
Generic a=> Behavior a
?Если не использовать GHC Generics, есть ли другие способы получить нужный мне API с минимальными трудностями для конечного пользователя (т. е. им просто нужно добавить производное предложение к своему типу)? например с
Data.Data
?
GHC.Generics
. Возможно, позже на этой неделе у меня будет время написать пример, если никто не доберется до него первым. - person Nathan Howell   schedule 20.11.2012