4

次のコードで関数のパラメーターの数を知ることができます

{-#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、いくつかの特殊な型で「終了」した関数に対して期待どおりに動作します。私の質問は、これを一般化して、任意の関数のパラメーター番号を伝えることができるでしょうか?

4

2 に答える 2

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
于 2013-06-26T13:01:05.607 に答える
0

GHC 7.6.1 で動作する型ファミリの方法を 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)))

しかし、この問題は、次のバージョンのいずれかで登場するように思われるクローズド タイプ ファミリで実際に解決できると思います。今、指定されたリンクでそれらの存在について読んだところですが、

type family GetF' f :: Nat where
  GetF' (a -> b) = 1 + (GetF' b)
  GetF' a = 0

動作するはずです。型の真のパターン マッチング、ついに!(そして、これがナンセンスであるとすれば、私はそれらについてもう少し説明したいと思います。)

于 2013-07-03T12:39:15.077 に答える