代码之家  ›  专栏  ›  技术社区  ›  Mathias Vorreiter Pedersen

rpc(或者:如何根据typerep值消除函数应用程序的歧义?)

  •  3
  • Mathias Vorreiter Pedersen  · 技术社区  · 6 年前

    我正在为在haskell中进行远程过程调用构建一些基础设施,由于在这里解释的时间太长,我无法重用现有的库。

    设置如下:我有一个用于序列化和反序列化数据的类型类:

    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 我需要知道存在主义类型seriableext包装的具体类型。

    所以我实施 encode :: SerializableExt -> B.ByteString 将混凝土类型与值序列化时:

    encode (SerializableExt x) = encode (typeOf x, x)
    

    使用 typeOf Data-Typeable .现在的问题是 解码::b.bytesting->可能是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
    

    但这是不好的,原因有几个:

    1. 扩展是很乏味的。
    2. 它不能一般地处理成对的(或一般的:元组);每个 需要处理类型组合。
    3. 不是很像哈斯凯利
    1 回复  |  直到 6 年前
        1
  •  1
  •   bergey    6 年前

    Data.Dynamic 让我们将任意的haskell值放入一个容器中,然后以一种类型安全的方式再次将其取出。这是进程间通信的良好开端;我将回到下面的序列化。

    我们可以写一个程序 Dynamic 值,检查它所需的数字和类型,并以相同的方式返回结果。

    {-# 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 指导类实例的选择。(为了便于测试我的代码,我使用 String (第页)

    class Serial a where
        encode :: a -> String
        decode :: String -> Maybe a
    
    decodeAs :: Serial a => TypeRep a -> String -> Maybe a
    decodeAs _ s = decode s
    

    最后,我们要将 类型代表 ,解码时,请检查编码类型是否与我们正在解码的类型匹配。

    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
    
    推荐文章