2013-12-07 2 views
8

У меня есть функция, которая по заданной Name функции она дополняющая ее, получая еще одну функции, применяемой к какому-либо другому материалу (подробности не очень уместно):Шаблон Haskell: Как извлечь количество аргументов функции?

mkSimple :: Name -> Int -> Q [Dec] 
mkSimple adapteeName argsNum = do 
    adapterName <- newName ("sfml" ++ (capitalize . nameBase $ adapteeName)) 
    adapteeFn <- varE adapteeName 
    let args = mkArgs argsNum 
    let wrapper = mkApply adapteeFn (map VarE args) 
    -- generates something like SFML $ liftIO $ ((f a) b) c) 
    fnBody <- [| SFML $ liftIO $ $(return wrapper) |] 
    return [FunD adapterName [Clause (map VarP args) (NormalB fnBody) []]] 

    where 
    mkArgs :: Int -> [Name] 
    mkArgs n = map (mkName . (:[])) . take n $ ['a' .. 'z'] 


-- Given f and its args (e.g. x y z) builds ((f x) y) z) 
mkApply :: Exp -> [Exp] -> Exp 
mkApply fn [] = fn 
mkApply fn (x:xs) = foldr (\ e acc -> AppE acc e) (AppE fn x) xs 

Это работает, но это утомительно, чтобы пройти внешнее число аргументов, функция адаптирования имеет. Существует некоторая функция TH для извлечения числа аргументов? Я подозреваю, что это может быть достигнуто с reify, но я не знаю как.

Спасибо!

ответ

6

Конечно, вы должны быть в состоянии сделать

do (VarI _ t _ _) <- reify adapteeName 
    -- t :: Type 
    -- e.g. AppT (AppT ArrowT (VarT a)) (VarT b) 
    let argsNum = countTheTopLevelArrowTs t 
    ... 

    where 
    countTheTopLevelArrowTs (AppT (AppT ArrowT _) ts) = 1 + countTheTopLevelArrowTs 
    countTheTopLevelArrowTs _ = 0 

Вышеуказанное как раз из моей головы и может быть не совсем правильно.

+0

достаточно просто, несмотря на то, извлекая количество стрелок кажется громоздким и вовлекая много шаблонов матчей. Это можно сделать рекурсивным, но я не вижу простой базовый регистр для рекурсии. Есть идеи? Очевидно, что-то вроде T.count «ArrowT» (show t) подвержено ошибкам (например, если у вас есть :: (a -> b) -> c -> d, счет будет неправильным) –

+0

@AlfredoDiNapoli жаль, что ленился с ' countTheTopLevelArrowTs' в моем ответе. Это действительно громоздко, но не должно быть двусмысленным: обратите внимание, что вам не нужно рекогносцировать первые аргументы любого из «ArrowT». Я обновлю свой ответ. – jberryman

+2

Это неоднозначно. Сколько аргументов в функция с подписью 'Arrow a => Int -> a Int Int'? Полиморфизм означает, что всегда будет какая-то двусмысленность. – Carl

1

Небольшое улучшение на ответ jberryman, что имеет дело с ограничениями типа, таких как (Ord a) -> a -> a является:

arity :: Type -> Integer 
arity = \case 
    ForallT _ _ rest -> arity rest 
    AppT (AppT ArrowT _) rest -> arity rest +1 
    _ -> 0 

использование:

do (VarI _ t _ _) <- reify adapteeName 
    let argsNum = arity t 
Смежные вопросы