我目前正在开发一个Haskell项目,它根据XML规范自动测试一些函数。 XML规范给出了每个函数的参数以及函数将提供的预期结果(参数有许多不同的类型)。我知道如何从XML中提取函数参数并使用read函数解析它们,但我还没有弄清楚如何使用我得到的参数来调用函数。
我基本上想要的是在异构列表中读取和存储参数(我当前的想法是使用Data.Dynamic类型的列表),然后调用该函数,将此异构列表作为其参数列表传递。这可能吗?修改被测功能不是一种选择。
答案 0 :(得分:2)
我会建议Nathan建议使用Haskell作为规范语言。 Haskell本身是使用Haskell函数的最佳数据格式: - )
但是因为这是一个有趣的问题,我会假装你,通过一些奇怪的约束,必须使用XML。您将不得不将XML转换为真正的Haskell函数。这意味着要有一些映射:
lookupFunc :: String -> ???
通过名字查找功能。您必须手动编写此映射,或使用Template Haskell生成它。但重要的是,???
不是一种类型,而且这个函数需要一个真实的类型。
这是一个很好的,类似于您的异构列表,但更加优化了手头的问题:
data SpecFunc = Result String | More (String -> SpecFunc)
这是您对XML规范的接口。它说我已经完成并且已经有结果(已经被字符串化)或者我需要另一个参数来继续(从字符串转换到该函数)。 (令人讨厌的旁注:这被称为“免费monad over(String - >)” - 但是monadiness现在与我们无关)。
现在我们可以编写一个类型类来将Haskell函数转换为它们的SpecFuncs,如果它们的类型符合我们的标准:
class HasSpecFunc a where
toSpecFunc :: a -> SpecFunc
instance (Read a, HasSpecFunc b) => HasSpecFunc (a -> b) where
toSpecFunc f = More (\input -> toSpecFunc (f (read input)))
... -- one of these for each one of your "primitive" result types
instance HasSpecFunc String where
toSpecFunc x = Result (show x)
使用一些邪恶可以避免必须为每个结果类型指定一个实例。在文件顶部,启用重叠实例:
{-# LANGUAGE OverlappingInstances #-}
然后使用:
instance (Show a) => HasSpecFunc a where
toSpecFunc x = Result (show x)
然后你可以使用类似的东西调用SpecFunc:
-- returns Nothing if the wrong number of arguments were provided
runSpecFunc :: SpecFunc -> [String] -> Maybe String
runSpecFunc (Result x) [] = Just x
runSpecFunc (More f) (x:xs) = runSpecFunc (f x) xs
runSpecFunc _ _ = Nothing
我希望这是有道理的。但是,再次抛弃XML并使用Haskell远比这更好。
答案 1 :(得分:0)
使用Data.Dynamic通常是一个坏主意,因为你牺牲了GHC为你检查参数的能力。您几乎总是知道类型必须支持的有效函数,因此您可以始终使用存在类型列表。
相反,是否可以构建一个表示XML将生成的有效类型的数据类型?也就是说,构建一个表示格式良好的XML参数的抽象语法的ADT。
例如,查看JSON ADT:http://hackage.haskell.org/packages/archive/json/0.4.3/doc/html/src/Text-JSON-Types.html#JSValue
或者,使用存在类型(静态)确保值支持您为它们声明的方法。例如,XML中的所有值都必须支持Show,因此您可以将异构列表描述为:
data XMLList = forall a。显示a => XMLList [a])
来自Haskell wikibook的一个例子:http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types#Example:_heterogeneous_lists