문제

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?

도움이 되었습니까?

해결책

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

다른 팁

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.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top