Pull to refresh

Comments 23

Haskell автоматически выполняет каррирование всех функций, принимающих более одного параметра.
Скорее, программист обычно выполняет каррирование всех функций, принимающих более одного параметра. Но можно и не выполнять. Так, функция, принимающая кортеж — это тоже, в некотором роде, функция нескольких параметров — при этом определение такой функции ничуть не сложнее определения каррированной функции.

add1 a b = a+b
add2 (a, b) = a+b
Что интересно, для получения одного из другого можно использовать встроенные функции curry и uncurry, что довольно удобно для выполнения всяких операций над парами.

Например,
map (uncurry (+)) xs
превращает список пар в список их сумм.

Надо понимать, что функции эти действуют для двух аргументов, больше — либо самому писать, либо удовлетворяться встроенными, которые, впрочем, есть и для 3х аргументов: hackage.haskell.org/package/utility-ht-0.0.5.1/docs/Data-Tuple-HT.html
В Haskell функции без параметров называются константными функциями, поскольку каждая из них всегда возвращает одно и то же значение.

Это неправильно. В Haskell нет функций без параметров. В сигнатуре функции (после всех возможных подстановок) всегда присутствует хотя бы одна стрелка; если это не так — то это не функция, это конкретное значение.
М. Липовача «Изучай Haskell во имя добра!», стр. 28:
Когда функция не принимает аргументов, говорят, что это константная функция.
Формально это всё биндинги и разницы нет.
На самом деле, этот вопрос сложнее, чем Вам кажется. Элиот посвятил этому вопросу целый пост, который я рекомендую к прочтению.

Googolplex прав насчёт того, что в Haskell нет функций без параметров. И этот вопрос не связан с тем, есть ли у выражения биндинг или нет. Не все функции являются биндингами. Не все значения являются биндингами. Эта формулировка вообще некорректна. Некоторые выражения связаны с именами. Некоторые — нет. Но у каждого выражения есть тип. При этом некоторые выражения имеют тип функции, другие — нет.

Думаю, Миран Липовача осознанно опускает различие между функциями и биндингами (см. «Mixing up functions and definitions» у Элиота). Мне кажется, что на стр. 28 книги для начинающих это вполне допустимо. Пусть человек «пощупает» язык, а в терминологии он разберётся и потом.
Благодарю за ссылку, почитаю. Мои заметки по Haskell — это изложение того, как я понял ту или иную тему в ходе её изучения. Я не исключаю, что какой-то материал мною мог быть понят неверно. Опубликовывая информацию по Haskell через призму своего текущего понимания, я тем самым ожидаю, что более опытные люди смогут аргументированно указать мне на мои ошибки (собственно ради этого и опубликовываю).
В книгах тоже иногда пишут фигню.
Заглянул в оригинал:
When a function doesn't take any parameters, we usually say it's a definition (or a name).

Как видим, перевод действительно отличается…
Что такое «конкретное значение» в данном контексте?
Экземпляр конкретного типа данных (Int, String, Bool, etc).
Как отличить конкретный тип данных от абстрактного? неконкретного?
М. Липовача «Изучай Haskell во имя добра!», стр 159:
Примечание. Мы называем тип конкретным, если он вообще не принимает параметров (например Int или Bool) либо если параметры в типе заполнены (например, Maybe Char). Если у вас есть какое-то значение, у него всегда конкретный тип.
Тогда противопоставление «функции» «конкретному значению» некорректно.
Все параметры в типе ''Int -> Bool'' заполнены. Следовательно, он является конкретным.
Тогда функция, это просто (->) (с аргументами, разумеется, так как сама стрелка имеет kind * -> * -> *), а всё остальное — не функция.

Т.е. это не функция:
Maybe (a -> b) -- == Maybe ((->) a b)

Зато это — функция:
a -> Maybe b -- == (->) a (Maybe b)


Именно так и есть. Вы правы. 'Maybe (a -> b)' — не функция.
Поэтому-то противопоставление «функций» «конкретным значениям» несколько странновато, не находите?
Да, странновато. Куда логичнее выделять CAF'ы, ибо они
can either be compiled to a piece of graph which will be shared by all uses or to some shared code which will overwrite itself with some graph the first time it is evaluated

Но они при этом вовсе не обязаны иметь тип не функции.
Бессмысленно разделять возвращаемые значения функции на CAF-ы и не-CAF-ы.
Уж в контексте каррирования точно.
Это значит — константа, элемент из множества нефункционального типа:
pi :: Double
pi = 3.14   // вещественная константа

printHello :: IO ()
printHello = putStrLn "Hello"  // константа - IO-действие


Определение нефункционального типа, я полагаю, можно ввести так.

Функциональный тип — это экспоненциал в категории Hask. Соответственно, нефункциональные типы составляют дополнение функциональных типов до всего класса объектов Hask, т. е. это все те объекты, которые не являются экспоненциалами.
Прекрасное в своей витиеватости определение, абсолютно эквивалентное «конкретное значение — всё, что не функция».
Тема бесточечной нотации вообще не раскрыта. Лучше было бы сделать пример каким-то таким:
countEven :: Integral a => [a] -> Int
countEven = length . filter even

И потом объяснить, что под точкой подразумевается совсем не знак композиции.
Надо бы в сигнатуру добавить еще одну переменную типа
func :: (Num a) => a -> a -> a -> a
func a b c d = a + b + c + d

Комментарий совершенно верный. Непонятно, кому что не понравилось. Должно быть


func :: (Num a) => a -> a -> a -> a -> a
func a b c d = a + b + c + d
Sign up to leave a comment.

Articles