Можно ли написать функцию arity :: a -> Integer
, чтобы определить арность произвольных функций, такую, что
> arity map
2
> arity foldr
3
> arity id
1
> arity "hello"
0
?
Можно ли написать функцию arity :: a -> Integer
, чтобы определить арность произвольных функций, такую, что
> arity map
2
> arity foldr
3
> arity id
1
> arity "hello"
0
?
Легко с OverlappingInstances
:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Arity f where
arity :: f -> Int
instance Arity x where
arity _ = 0
instance Arity f => Arity ((->) a f) where
arity f = 1 + arity (f undefined)
Обновление. Для полиморфных функций вам необходимо указать неполиморфный тип:
arity (foldr :: (a -> Int -> Int) -> Int -> [a] -> Int)
Не знаю, как это решить.
Upd2, как прокомментировал Sjoerd Visscher ниже, "вы должны указать неполиморфный тип, поскольку ответ зависит от того, какой тип вы выберете".
Да, это можно сделать очень, очень легко:
arity :: (a -> b) -> Int
arity = const 1
Обоснование: если это функция, вы можете применить ее к точно одному аргументу. Обратите внимание, что синтаксис haskell делает невозможным применение к 0, 2 или более аргументам, поскольку f a b
действительно (f a) b
, то есть не f applied to a and b
, а (f applied to a) applied to b
.
Результатом может быть, конечно, еще одна функция, которую можно применить снова и т.д.
Звучит глупо, но это не что иное, как правда.
Если id
имеет arity 1, не должно id x
иметь arity 0? Но, например, id map
идентичен map
, который имеет в вашем примере arity 2.
Имеют ли следующие функции одинаковые свойства?
f1 = (+)
f2 = (\x y -> x + y)
f3 x y = x + y
Я думаю, что ваше понятие "arity" не совсем определено...
Это невозможно со стандартным Haskell. Это может быть возможно с использованием IncoherentInstances или аналогичного расширения.
Но почему вы хотите это сделать? Вы не можете спросить функцию, сколько аргументов она ожидает, а затем использовать это знание, чтобы дать ему именно такое количество аргументов. (Если вы не используете Template Haskell, в этом случае, да, я ожидаю, что это возможно во время компиляции. Используете ли вы Template Haskell?)
Какова ваша фактическая проблема, которую вы пытаетесь решить?
В Haskell каждая "функция" принимает ровно один аргумент. То, что выглядит как функция с несколькими аргументами, фактически является функцией, которая принимает один аргумент и возвращает другую функцию, которая принимает остальные аргументы. Таким образом, в этом смысле все функции имеют arity 1.
Как насчет этого:
arity :: a -> Int
arity (b->c) = 1 + arity (c)
arity _ = 0