我有以下数据结构:
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
。我可以单独解析这些部分,用 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 中执行此操作的最佳方法是什么?
答案 0 :(得分:3)
给定一个 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
进行相等测试。
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