我可以用以下代码告诉函数的参数个数
{-#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
我们得到:
*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)))
正如您所看到的,functionLevel
正如我们所期望的那样执行某些特殊类型的“结束”功能。我的问题是:我们可以推广这个来告诉任意函数的参数号吗?
答案 0 :(得分:4)
只是一个想法;您可以使用以下代码在值级别告诉函数的参数数量。
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
答案 1 :(得分:0)
我找到了一种类型系列的方法,它适用于GHC 7.6.1。但是,这有点像黑客,因为决定什么是“结束类型”的问题通过用包装器标记来解决:
-- 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
给出了
*Main> :t undefined :: Forget (GetF (Int -> (Char -> Char -> V Bool)))
undefined :: Forget (GetF (Int -> (Char -> Char -> V Bool)))
:: Forget Nat (1 + (1 + (1 + 0)))
但我认为问题可以通过closed type families真正解决,这似乎出现在下一个版本中。现在,我刚刚在给定的链接中了解了它们的存在,但是
type family GetF' f :: Nat where
GetF' (a -> b) = 1 + (GetF' b)
GetF' a = 0
看起来应该可行。最后,类型上的真实模式匹配! (如果这是无稽之谈,我会对它们的更多解释感兴趣。)