RPC(或者:如何根据 TypeRep 值消除函数应用程序的歧义?)
RPC (Or: How do I disambiguate function application based on TypeRep values?)
我正在构建一些用于在 Haskell 中执行远程过程调用的基础结构,由于太长的原因无法在此处解释,我无法重用现有的库。
设置如下:我有一个类型 class 用于序列化和反序列化数据:
class Serializable a where
encode :: a -> B.ByteString
decode :: B.ByteString -> Maybe a
maxSize :: a -> Int
其中 B
是 Data.ByteString。
我可以用它来实现整数、布尔值、可序列化列表、可序列化元组等的序列化。
现在我想通过网络将一些参数发送到服务器,然后服务器根据这些参数执行计算,并发回结果。所以我创建了一个存在类型来表示可以序列化的东西:
data SerializableExt = forall t . Serializable t => SerializableExt t
因为我想发送 [SerializableExt]
.
类型的东西
所以,当然,我需要创建一个实例Serializable SerializableExt
。这是问题开始的地方:
为了实现 decode :: B.ByteString -> Maybe SerializableExt
我需要知道存在类型 SerializableExt 包装的具体类型。
所以我将 encode :: SerializableExt -> B.ByteString
实现为将具体类型与值一起序列化:
encode (SerializableExt x) = encode (typeOf x, x)
使用 Data-Typeable 中的 typeOf
。现在的问题是 decode :: B.ByteString -> Maybe SerializableExt
:
的执行
decode bs =
let (tyenc, xenc) = splitPair bs -- Not really important. It just splits bs into the two components
in case (decode tyenc :: Maybe TypeRep) of
Just ty -> SerializableExt <$> _ -- Somehow invoke decode xenc, where the choice of which decode to execute depends on the value of ty.
_ -> Nothing
但是我看不出怎么填这里的洞。由于 Haskell 值级别和类型级别的分离,我不能使用 ty 的值来消除 decode xenc
调用的歧义,对吗?
有没有办法解决这个问题,然后在洞里放点东西,就能达到我想要的效果?或者你能想出另一种设计吗?
编辑:一种方法如下:
decode bs =
let (tyenc, xenc) = splitPair bs
in SerializableExt <$>
case (decode tyenc :: Maybe TypeRep) of
Just ty
| ty == typeRep (Proxy :: Proxy Int) -> decode xenc :: Maybe Int
| ty = typeRep (Proxy :: Proxy ()) -> decode xenc :: Maybe ()
| ...
_ -> Nothing
但这很糟糕,原因有几个:
- 扩展起来很乏味。
- 它不能一般地处理对(或通常:元组);每一个
需要处理类型的组合。
- 不是很Haskelly
Data.Dynamic
允许我们将任意 Haskell 值放入单个容器中,然后以类型安全的方式再次将它们取出。这是进程间通信的良好开端;下面我会回来连载的。
我们可以编写一个程序,获取 Dynamic
个值的列表,检查所需的数字和类型,并以相同的方式 returns 结果。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Experiments with type-safe serialization.
module Main where
import Data.Proxy
import Data.Dynamic
import Data.Foldable
import Data.Type.Equality
import Type.Reflection
foo :: Int -> String -> String
foo i s = concat (replicate i s)
actor :: [Dynamic] -> Either String [Dynamic]
actor (di : ds : _) = case (fromDynamic di, fromDynamic ds) of
(Just i, Just s) -> Right [toDyn (foo i s)]
_ -> Left "Wrong types of arguments"
actor _ = Left "Not enough arguments"
caller :: Either String [Dynamic]
caller = actor [ toDyn (3::Int), toDyn "bar" ]
main :: IO ()
main = case caller of
Left err -> putStrLn err
Right dyns -> for_ dyns (\d -> case fromDynamic d of
Just s -> putStrLn s
Nothing -> print d)
我们可以使用 TypeRep
来指导选择 class 实例。 (为了便于测试我的代码,我使用了 String
。)
class Serial a where
encode :: a -> String
decode :: String -> Maybe a
decodeAs :: Serial a => TypeRep a -> String -> Maybe a
decodeAs _ s = decode s
最后,我们要序列化 TypeRep
,并在解码时检查编码类型是否与我们正在解码的类型匹配。
instance Serial SomeTypeRep
encodeDyn :: (Typeable a, Serial a) => a -> (String, String)
encodeDyn a = (encode (SomeTypeRep (typeOf a)), encode a)
decodeDynamic :: forall a. (Typeable a, Serial a) => String -> String -> Maybe a
decodeDynamic tyStr aStr = case decode tyStr of
Nothing -> Nothing
Just (SomeTypeRep ty) ->
case eqTypeRep ty (typeRep :: TypeRep a) of
Nothing -> Nothing
Just HRefl -> decodeAs ty aStr
我正在构建一些用于在 Haskell 中执行远程过程调用的基础结构,由于太长的原因无法在此处解释,我无法重用现有的库。
设置如下:我有一个类型 class 用于序列化和反序列化数据:
class Serializable a where
encode :: a -> B.ByteString
decode :: B.ByteString -> Maybe a
maxSize :: a -> Int
其中 B
是 Data.ByteString。
我可以用它来实现整数、布尔值、可序列化列表、可序列化元组等的序列化。
现在我想通过网络将一些参数发送到服务器,然后服务器根据这些参数执行计算,并发回结果。所以我创建了一个存在类型来表示可以序列化的东西:
data SerializableExt = forall t . Serializable t => SerializableExt t
因为我想发送 [SerializableExt]
.
所以,当然,我需要创建一个实例Serializable SerializableExt
。这是问题开始的地方:
为了实现 decode :: B.ByteString -> Maybe SerializableExt
我需要知道存在类型 SerializableExt 包装的具体类型。
所以我将 encode :: SerializableExt -> B.ByteString
实现为将具体类型与值一起序列化:
encode (SerializableExt x) = encode (typeOf x, x)
使用 Data-Typeable 中的 typeOf
。现在的问题是 decode :: B.ByteString -> Maybe SerializableExt
:
decode bs =
let (tyenc, xenc) = splitPair bs -- Not really important. It just splits bs into the two components
in case (decode tyenc :: Maybe TypeRep) of
Just ty -> SerializableExt <$> _ -- Somehow invoke decode xenc, where the choice of which decode to execute depends on the value of ty.
_ -> Nothing
但是我看不出怎么填这里的洞。由于 Haskell 值级别和类型级别的分离,我不能使用 ty 的值来消除 decode xenc
调用的歧义,对吗?
有没有办法解决这个问题,然后在洞里放点东西,就能达到我想要的效果?或者你能想出另一种设计吗?
编辑:一种方法如下:
decode bs =
let (tyenc, xenc) = splitPair bs
in SerializableExt <$>
case (decode tyenc :: Maybe TypeRep) of
Just ty
| ty == typeRep (Proxy :: Proxy Int) -> decode xenc :: Maybe Int
| ty = typeRep (Proxy :: Proxy ()) -> decode xenc :: Maybe ()
| ...
_ -> Nothing
但这很糟糕,原因有几个:
- 扩展起来很乏味。
- 它不能一般地处理对(或通常:元组);每一个 需要处理类型的组合。
- 不是很Haskelly
Data.Dynamic
允许我们将任意 Haskell 值放入单个容器中,然后以类型安全的方式再次将它们取出。这是进程间通信的良好开端;下面我会回来连载的。
我们可以编写一个程序,获取 Dynamic
个值的列表,检查所需的数字和类型,并以相同的方式 returns 结果。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Experiments with type-safe serialization.
module Main where
import Data.Proxy
import Data.Dynamic
import Data.Foldable
import Data.Type.Equality
import Type.Reflection
foo :: Int -> String -> String
foo i s = concat (replicate i s)
actor :: [Dynamic] -> Either String [Dynamic]
actor (di : ds : _) = case (fromDynamic di, fromDynamic ds) of
(Just i, Just s) -> Right [toDyn (foo i s)]
_ -> Left "Wrong types of arguments"
actor _ = Left "Not enough arguments"
caller :: Either String [Dynamic]
caller = actor [ toDyn (3::Int), toDyn "bar" ]
main :: IO ()
main = case caller of
Left err -> putStrLn err
Right dyns -> for_ dyns (\d -> case fromDynamic d of
Just s -> putStrLn s
Nothing -> print d)
我们可以使用 TypeRep
来指导选择 class 实例。 (为了便于测试我的代码,我使用了 String
。)
class Serial a where
encode :: a -> String
decode :: String -> Maybe a
decodeAs :: Serial a => TypeRep a -> String -> Maybe a
decodeAs _ s = decode s
最后,我们要序列化 TypeRep
,并在解码时检查编码类型是否与我们正在解码的类型匹配。
instance Serial SomeTypeRep
encodeDyn :: (Typeable a, Serial a) => a -> (String, String)
encodeDyn a = (encode (SomeTypeRep (typeOf a)), encode a)
decodeDynamic :: forall a. (Typeable a, Serial a) => String -> String -> Maybe a
decodeDynamic tyStr aStr = case decode tyStr of
Nothing -> Nothing
Just (SomeTypeRep ty) ->
case eqTypeRep ty (typeRep :: TypeRep a) of
Nothing -> Nothing
Just HRefl -> decodeAs ty aStr