Is there a general way to tell the number of parameters of a function in Haskell?

為{幸葍}努か 提交于 2019-12-07 08:59:50

问题


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?


回答1:


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



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



来源:https://stackoverflow.com/questions/17318107/is-there-a-general-way-to-tell-the-number-of-parameters-of-a-function-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!