问题描述
我想为 Reflex 实现一个多层库,您可以在其中将客户端-服务器程序编写为单个值。 我遇到的问题是客户端和服务器具有不同的能力,但接口的客户端实现不应该支持服务器能力,反之亦然。
我可以想象这样一个(简化的)“WebM”类型类,具有明确分离的客户端和服务器类型:
class (Reflex c,Reflex s) => WebM c s m where
type CM m :: * -> * -- Client context
type SM m :: * -> * -- Server context
liftClient :: CM m a -> m a
liftServer :: SM m a -> m a
-- Event occurrences received at client.
toClient :: Event s a -> m (Event c a)
-- Event occurrences received at server.
toServer :: Event c a -> m (Event s a)
...
在这个 WebM
monad 中,程序可能会要求服务器上下文支持数据库实现,并要求客户端支持 GUI:
example :: ( HasGui c (CM m),HasDatabase s (SM m),WebM c s m
) => m ()
example = do
-- Uses GUI
queries :: Event c Query <-
liftClient getInput
queriesAtServer :: Event s Query <-
toServer queries
-- uses DB
queryResults :: Event s Result <-
liftServer (getResults queriesAtServer)
-- Etc.
...
我第一次想到实现例如客户将在现有的 Reflex 时间线 t
和 monad m
上编写一个转换器,并为其提供 WebM
实现。
由于 WebM
类型类是如何编写来分隔层的,因此不可能触及“服务器”类型的值,因此程序对服务器的要求无关紧要,只要它需要什么客户要求。
我可以在下面的 ???
点填写什么来实现这一目标,或者我可以使用更好的策略吗?
newtype ClientT t m a = ClientT ...
instance (Reflex t) => WebM t ??? (ClientT t m) where
type CM (ClientT t m) = m
type SM (ClientT t m) = ??? -- This can safely be anything
toServer e = do
-- Send messages to server
pure ??? -- Absurd value.
toClient e = do
-- Listen for messages from server
...
我最初尝试了 Reflex 的“Void”实现(下面的尝试),但您仍然必须为程序可能会限制服务器/客户端实现的任何和所有类型类编写实例。这看起来很乏味。
data Voidflex :: *
newtype VoidM a = VoidM Void
instance Reflex Voidflex where
newtype Behavior Voidflex a = Behavior { unB :: Void }
newtype Event Voidflex a = Event { unE :: Void }
newtype Dynamic Voidflex a = Dynamic { unD :: Void }
type PullM Voidflex = VoidM
type PushM Voidflex = VoidM
...
-- Would like to avoid:
instance HasDatabase Voidflex VoidM where
...
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)