如何在解析 json 时从多个 GADT 构造值?

How to construct values from multiple GADTs while parsing json?

我有以下数据结构:

data Operator :: * -> * where
  StringEquals     :: Operator String
  StringNotEquals  :: Operator String
  NumericEquals    :: Operator Integer
  NumericNotEquals :: Operator Integer

data Variable :: * -> * where
  UserName     :: Variable String
  RequestPath  :: Variable String
  BodyLength   :: Variable Integer

data Value :: * -> * where
  StringValue     :: String -> Value String
  NumericValue    :: Integer -> Value Integer

data Condition a = Condition (Operator a) (Variable a) (Value a)

将在如下函数中使用:

extractVariable :: Variable a -> HttpRequest -> a
evaluate :: Operator a -> Value a -> a -> Bool

我想从 JSON 文件中解析 Conditions。我可以单独解析用 Some 包裹的部分,但不确定如何从它们构建 Condition。 以下工作并显示了我想做的事情,但它显然不可扩展来编写:

import Data.Some

parseCondition :: Some Operator -> Some Variable -> Some Value -> Parser (Some Condition)
parseCondition (Some op@StringEquals) (Some var@UserName) (Some val@(StringValue _)) = return $ mkSome $ Condition op var val
parseCondition (Some op@StringNotEquals) (Some var@UserName) (Some val@(StringValue _)) = return $ mkSome $ Condition op var val
parseCondition (Some op@StringEquals) (Some var@RequestPath) (Some val@(StringValue _)) = return $ mkSome $ Condition op var val
parseCondition (Some op@StringNotEquals) (Some var@RequestPath) (Some val@(StringValue _)) = return $ mkSome $ Condition op var val
parseCondition (Some op@NumericEquals) (Some var@BodyLength) (Some val@(NumericValue _)) = return $ mkSome $ Condition op var val
parseCondition _ _ _ = parseFail "incompatible types"

在 haskell 中执行此操作的最佳方法是什么?

给定一个 Operator a(分别为 Variable aValue a),您确切地知道 a 是什么。使用表示类型 a 的完整知识的标准类型是 TypeRep a,用代码表达该知识。

import Type.Reflection -- NOT the other TypeRep

operatorType :: Operator a -> TypeRep a
operatorType StringEquals = typeRep
operatorType StringNotEquals = typeRep
operatorType NumericEquals = typeRep
operatorType NumericNotEquals = typeRep

variableType :: Variable a -> TypeRep a
variableType UserName = typeRep
variableType RequestPath = typeRep
variableType BodyLength = typeRep

valueType :: Value a -> TypeRep a
valueType (StringValue _) = typeRep
valueType (NumericValue _) = typeRep

这是很多样板文件,但我们的想法是它现在扩展为每个构造函数一行,而不是每个三个构造函数的可能组合一行。此外,它看起来可以自动化,但我不知道有任何“预制”解决方案。

现在只需对 TypeReps 使用等式测试。

import Data.Type.Equality
-- data a :~: b where Refl :: a :~: a
-- testEquality :: TypeRep a -> TypeRep b -> Maybe (a :~: b)

-- data Some f = forall a. Some (f a) -- I guess?
makeCondition :: Some Operator -> Some Variable -> Some Value -> Maybe (Some Condition)
makeCondition (Some op) (Some v) (Some x) = do -- I generally prefer guards for this "proofwork" but in this case a do happens to work
  Refl <- operatorType op `testEquality` variableType v
  Refl <- variableType v `testEquality` valueType x
  return $ Some $ Condition op v x