Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection on inputs to a function in Haskell?

Tags:

haskell

I have a reflective situation where I want to display the types of inputs/outputs of a function. I could just add it to a separate data structure, but then I have duplication and would have to make sure they stay in sync manually.

For example, a function:

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

and so now I want to have something like (can be somewhat flexible, esp. when params involved):

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

defined automatically. It doesn't have to be Strings directly. Is there a simple way to do this?

like image 804
mentics Avatar asked Dec 29 '22 00:12

mentics


1 Answers

You don't really need a custom parser. You can just reflect on the TypeRep value you receive. For instance the following would work:

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

and a sample session

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]]

This is just some quick barely tested code, I'm sure it can be cleaned up. And the reason I don't use typeRepArgs is that in the case of other constructors they would be broken up to, e.g [] Char returns Char instead of [Char].

This version does not treat elements of tuples as seperate results, but it can be easily changed to add that.

However as mentioned before this has a limitation, It'll only work on monomorphic types. If you want to be able to find this for any type, then you should probably use the GHC api. You can load the module, ask it to Typecheck and then inspect the Typecheck AST for the function and thus find it's type. Or you can use Hint to ask for the type of a function. and parse that.

like image 187
Phyx Avatar answered Feb 06 '23 12:02

Phyx