对Haskell函数输入的反思?

时间:2011-02-28 16:40:19

标签: haskell

我有一个反思的情况,我想显示一个功能的输入/输出类型。我可以将它添加到一个单独的数据结构中,但之后我有重复,并且必须确保它们保持手动同步。

例如,一个函数:

myFunc :: (String, MyType, Double) -> (Int, SomeType TypeP, MyOtherType a)

所以现在我希望有类似的东西(可以有点灵活,特别是涉及参数时):

input = ["String", "MyType", "Double"]
output = ["Int", "SomeType TypeP", "MyOtherType a"]

自动定义。它不一定是直接的字符串。有一种简单的方法可以做到这一点吗?

2 个答案:

答案 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 -> TypeRepTypeRepShow的实例。例如:

$ 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将失败。