我有一个反思的情况,我想显示一个功能的输入/输出类型。我可以将它添加到一个单独的数据结构中,但之后我有重复,并且必须确保它们保持手动同步。
例如,一个函数:
myFunc :: (String, MyType, Double) -> (Int, SomeType TypeP, MyOtherType a)
所以现在我希望有类似的东西(可以有点灵活,特别是涉及参数时):
input = ["String", "MyType", "Double"]
output = ["Int", "SomeType TypeP", "MyOtherType a"]
自动定义。它不一定是直接的字符串。有一种简单的方法可以做到这一点吗?
答案 0 :(得分:7)
您真的不需要自定义解析器。您可以只反映您收到的TypeRep值。例如,以下内容可行:
module ModuleReflect where
import Data.Typeable
import Control.Arrow
foo :: (Int, Bool, String) -> String -> String
foo = undefined
-- | We first want to in the case that no result is returned from splitTyConApp
-- to just return the input type, this. If we find an arrow constructor (->)
-- we want to get the start of the list and then recurse on the tail if any.
-- if we get anything else, like [] Char then we want to return the original type
-- [Char]
values :: Typeable a => a -> [TypeRep]
values x = split (typeOf x)
where split t = case first tyConString (splitTyConApp t) of
(_ , []) -> [t]
("->", [x]) -> [x]
("->", x) -> let current = init x
next = last x
in current ++ split next
(_ , _) -> [t]
inputs :: Typeable a => a -> [TypeRep]
inputs x = case values x of
(x:xs) | not (null xs) -> x : init xs
_ -> []
output :: Typeable a => a -> TypeRep
output x = last $ values x
和示例会话
Ok, modules loaded: ModuleReflect.
*ModuleReflect Data.Function> values foo
[(Int,Bool,[Char]),[Char],[Char]]
*ModuleReflect Data.Function> output foo
[Char]
*ModuleReflect Data.Function> inputs foo
[(Int,Bool,[Char]),[Char]]
这只是一些经过严格测试的快速代码,我确信它可以清理干净。我不使用typeRepArgs的原因是,在其他构造函数的情况下,它们会被分解,例如[] Char返回Char而不是[Char]。
此版本不会将元组元素视为单独的结果,但可以轻松更改以添加该元素。
然而如前所述,这有一个限制,它只适用于单形类型。如果你想能够找到任何类型的这个,那么你应该使用GHC api。您可以加载模块,询问Typecheck,然后检查Typecheck AST的功能,从而找到它的类型。或者您可以使用提示来询问函数的类型。并解析。
答案 1 :(得分:3)
查看Data.Typeable
:它定义了一个函数typeOf :: Typeable a => a -> TypeRep
,TypeRep
是Show
的实例。例如:
$ ghci
GHCi, version 6.12.1: http://www.haskell.org/ghc/ :? for help
:m Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :m +Data.Typeable
Prelude Data.Typeable> :i TypeRep
data TypeRep
= Data.Typeable.TypeRep !Data.Typeable.Key TyCon [TypeRep]
-- Defined in Data.Typeable
instance [overlap ok] Eq TypeRep -- Defined in Data.Typeable
instance [overlap ok] Show TypeRep -- Defined in Data.Typeable
instance [overlap ok] Typeable TypeRep -- Defined in Data.Typeable
Prelude Data.Typeable> typeOf (+)
Integer -> Integer -> Integer
Prelude Data.Typeable> typeOf (\(a,(_:x),1) -> (a :: (),x :: [()]))
((),[()],Integer) -> ((),[()])
现在,您只需要一个自定义解析器,将此输出转换为适合您的输出。这留给读者练习。
PS:似乎有一个限制:所有类型必须先知道,例如typeOf id
将失败。