【发布时间】:2020-07-14 20:25:08
【问题描述】:
我正在实施Fibre 协议。协议的工作方式是您收到一个 json 对象,该对象代表您可以对远程对象执行的操作和值。下面是这样一个 json 的示例。
{
"name": "properties",
"type": "object",
"members": [
{
"name": "foo",
"id": 1,
"type": "uint32",
"access": "rw"
},
{
"name": "bar",
"id": 2,
"type": "uint32[]",
"access": "r"
},
{
"name": "bar",
"type": "object",
"members": [
{
"name": "baz",
"id": 3,
"type": "float",
"access": "rw"
},
{
"name": "some_function",
"id": 4,
"type": "function",
"inputs": [
{
"name": "param1",
"id": 5,
"type": "float",
"access": "rw"
},
{
"name": "param2",
"id": 6,
"type": "bool",
"access": "rw"
}
],
"outputs": [
{
"name": "result",
"id": 7,
"type": "bool",
"access": "rw"
}
]
}
]
}
]
}
可以看出,它本质上是一棵树,具有某些类型的属性和功能。有些属性只能读取,访问权限为r,有些属性可以读取/写入,访问权限为rw。通过一些数据定义,在 Haskell 中捕获这种结构很简单:
data FibreType
= FibreInt8 | FibreUInt8
| FibreInt16 | FibreUInt16
| FibreInt32 | FibreUInt32
| FibreInt64 | FibreUInt64
| FibreFloat
| FibreBool
| FibreJSON
| FibreList FibreType
deriving (Show, Eq, Ord)
data FibreAccess = FibreReadable | FibreReadWriteable
deriving (Show, Eq, Ord)
data FibreObject
= FibreValue
{ _fibreValueName :: String
, _fibreValueEndpoint :: Word16
, _fibreValueType :: FibreType
, _fibreValueAccess :: FibreAccess
}
| FibreFunction
{ _fibreFunctionName :: String
, _fibreFunctionEndpoint :: Word16
, _fibreFunctionArgs :: [FibreType]
, _fibreFunctionResult :: [FibreType]
}
| FibreObject
{ _fibreObjectName :: String
, _fibreObjectMembers :: [FibreObject]
}
deriving (Show, Eq, Ord)
getProperty :: FibreObject -> IO ByteString
getProperty FibreObject{} = error "Cannot get property of FibreObject"
getProperty FibreFunction{} = error "Cannot get property of FibreFunction"
getProperty FibreValue{} = undefined -- Implementation removed for brevity
setProperty :: FibreObject -> ByteString -> IO ByteString
setProperty FibreObject{} = error "Cannot set property of FibreObject"
setProperty FibreFunction{} = error "Cannot set property of FibreFunction"
setProperty FibreValue{_fibreValueAccess = FibreReadWriteable} = undefined -- Implementation removed for brevit
setProperty FibreValue{} = error "Cannot set property of read-only FibreValue"
callFunction :: FibreObject -> [ByteString] -> IO [ByteString]
callFunction FibreObject{} _ = error "Cannot call function for FibreObject"
callFunction FibreValue{} _ = error "Cannot call function for FibreObject"
callFunction FibreFunction{} args = undefined -- Implementation removed for brevit
但是我真的不喜欢这样,一旦我创建了函数 getProperty 或 callFunction 我就失去了所有类型安全性,我基本上必须通过使所有输入和输出 ByteString 来解决这个问题(或者我可以定义sum 类型),然后反序列化为某些具体值。所以我想知道是否有可能将此结构提升到类型级别,即使该 json 定义仅在运行时可用。
目标是我能写出这样的东西:
getProperty :: FibreValue type access -> IO type
getProperty FibreValue{} = undefined -- Implementation removed for brevity
setProperty :: FibreValue type FibreReadWriteable -> IO ()
setProperty FibreValue{} = undefined
callFunction :: FibreFunction [argTys] [resTys] -> argTys -> IO [resTys]
callFunction FibreValueFunction args = undefined -- Implementation removed for brevity
这将提供完整的类型安全性,而不必匹配实际上只能是单个值的结果类型。我发布了我为callFunction 给出的函数定义存在问题,但它更能说明这一点。
这在 Haskell 中可行吗?任何帮助表示赞赏。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
标签: haskell type-level-computation