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

Posted

技术标签:

【中文标题】RPC(或者:如何根据 TypeRep 值消除函数应用程序的歧义?)【英文标题】:RPC (Or: How do I disambiguate function application based on TypeRep values?) 【发布时间】:2018-07-02 20:54:02 【问题描述】:

我正在构建一些用于在 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,我需要知道存在类型 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

【问题讨论】:

我相信如果没有更多样板,这在 Haskell 中可能是不可能的。关键是,在运行时,我们会将这些位解码为TypeRep,用于某种类型的a,然后我们才需要访问Serializable a 的实例。要执行此操作,由于 a 在编译时是未知的(并且不是作为参数传递,而是从位中发现),我们需要在运行时拥有所有实例的列表,这是 GHC 不会保留的。 顺便说一句,我也相信可能有一些通用的方法来处理对。您应该能够检查TypeRep 是否用于pair 类型,并获得两个TypeReps 用于组件。之后,就可以递归了。 我感觉这是不可能的 :( 但是,您对配对的想法听起来很有趣!我不确定如何在不知道特定组件的情况下发现 TypeRep 是配对的类型,鉴于 Typeable 需要单态类型? 我认为即使组件未知***.com/questions/48951745/printing-dynamic-data,您也可以采用这种技术来检查对 【参考方案1】:

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

最后,我们要序列化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

【讨论】:

以上是关于RPC(或者:如何根据 TypeRep 值消除函数应用程序的歧义?)的主要内容,如果未能解决你的问题,请参考以下文章

Excel的数据透视表中,如何过滤value中的0值?

如何根据另一个变量的值使用 dplyr::Distinct

v8 是不是能够根据 `const` 的值消除死代码?

13RPC

从用户定义的函数中消除空查询结果

RPC介绍