如何在解析 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 文件中解析 Condition
s。我可以单独解析用 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 a
、Value 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
这是很多样板文件,但我们的想法是它现在扩展为每个构造函数一行,而不是每个三个构造函数的可能组合一行。此外,它看起来可以自动化,但我不知道有任何“预制”解决方案。
现在只需对 TypeRep
s 使用等式测试。
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
我有以下数据结构:
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 文件中解析 Condition
s。我可以单独解析用 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 a
、Value 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
这是很多样板文件,但我们的想法是它现在扩展为每个构造函数一行,而不是每个三个构造函数的可能组合一行。此外,它看起来可以自动化,但我不知道有任何“预制”解决方案。
现在只需对 TypeRep
s 使用等式测试。
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