Reputation: 1732
I can tell the number of parameters of a function with the following code
{-#Language MultiParamTypeClasses#-}
{-#Language FunctionalDependencies#-}
{-#Language UndecidableInstances#-}
data Zero
data Succ a
class Number a
instance Number Zero
instance (Number a) => Number (Succ a)
class NotFunction a
instance NotFunction Int
instance NotFunction Float
instance NotFunction (IO a)
class (Number n) => FunctionLevel f n | f -> n where
functionLevel :: f -> n
instance FunctionLevel Int Zero where
functionLevel = undefined
instance FunctionLevel Float Zero where
functionLevel = undefined
instance FunctionLevel (IO a) Zero where
functionLevel = undefined
instance FunctionLevel Double Zero where
functionLevel = undefined
instance (FunctionLevel f' n) => FunctionLevel (a->f') (Succ n) where
functionLevel = undefined
And we get:
*Main> :t functionLevel (undefined::a->b->Int)
functionLevel (undefined::a->b->Int) :: Succ (Succ Zero)
*Main> :t functionLevel (undefined::a->b->Double)
functionLevel (undefined::a->b->Double) :: Succ (Succ Zero)
*Main> :t functionLevel (undefined::a->b->c->d->IO a)
functionLevel (undefined::a->b->c->d->IO a)
:: Succ (Succ (Succ (Succ Zero)))
*Main> :t functionLevel (undefined::a->b->c->d->Int)
functionLevel (undefined::a->b->c->d->Int)
:: Succ (Succ (Succ (Succ Zero)))
As you can see, functionLevel
performs just as we expected for functions "ended" with some special types. My question is: could we generalize this to tell parameter number of an arbitrary function?
Upvotes: 4
Views: 224
Reputation: 20950
I found one way with type families, which works in GHC 7.6.1. It is a bit of a hack, though, since the problem of deciding what is the "ending type" is solved by marking it with a wrapper:
-- requires GHC.TypeLits, DataKinds, TypeFamilies and so on
newtype V a = V { unV :: a }
type family GetF f :: Nat
type instance GetF (V s) = 0
type instance GetF (a -> b) = 1 + (GetF b)
data Forget (x :: k) -- for showing types of kind Nat
which gives
*Main> :t undefined :: Forget (GetF (Int -> (Char -> Char -> V Bool)))
undefined :: Forget (GetF (Int -> (Char -> Char -> V Bool)))
:: Forget Nat (1 + (1 + (1 + 0)))
But I think the problem could really be solved with closed type families, which seem to come up in one of the next versions. Now, I've just read about their existence in the given link, but
type family GetF' f :: Nat where
GetF' (a -> b) = 1 + (GetF' b)
GetF' a = 0
looks like it should work. True pattern matching on types, finally! (And if this is nonsense, I would be really interested in some more explanation about them.)
Upvotes: 0
Reputation: 773
Just an idea; you can tell the number of parameters of a function at value level with the following code.
https://gist.github.com/nushio3/5867066
import Data.Typeable
import Test.Hspec
arityOf :: Typeable a => a -> Int
arityOf x = go $ typeOf x
where
go tr
| isFun $ typeRepTyCon tr = 1 + go (last $ snd $ splitTyConApp tr)
| otherwise = 0
funTyCon = typeRepTyCon $ typeOf ((1+):: Int -> Int)
isFun = (funTyCon ==)
main :: IO ()
main = hspec spec
func :: (Int -> Int) -> Int -> Int
func = undefined
spec :: Spec
spec = describe "arityOf" $ do
it "evaluates Integers correctly" $ arityOf (1::Int) `shouldBe` 0
it "evaluates Strings correctly" $ arityOf "(1::Int)" `shouldBe` 0
it "evaluates monads correctly" $ arityOf main `shouldBe` 0
it "evaluates multiplications correctly" $ arityOf ((*) :: Int -> Int -> Int)
`shouldBe` 2
it "is not deceived by non-tail argument" $ arityOf func `shouldBe` 2
Upvotes: 5