В чистых функциональных языках данные (строки, ints, floats..) также просто функционируют?

Я думал о чистых объектно-ориентированных языках, таких как Ruby, где все, включая числа, int, float и string, сами являются объектами. Это то же самое с чистыми функциональными языками? Например, в Haskell также действуют Numbers и Strings?

Я знаю, что Haskell основан на исчислении лямбда, который представляет все, включая данные и операции, как функции. Мне казалось логичным, что "чисто функциональный язык" будет моделировать все как функцию, а также придерживаться определения, что функция всегда возвращает один и тот же вывод с теми же входами и не имеет состояния.

Ответ 2

@wrhall дает хороший ответ. Однако вы несколько верны, что в чистом лямбда-исчислении для всех является последовательной функцией, а язык является Тьюрингом-полным (способным выражать любые чистые вычисления, которые Haskell и т.д.).

Это дает вам очень странные вещи, поскольку единственное, что вы можете сделать, - это применить его к чему-то другому. Когда вы когда-нибудь заметите что-нибудь? У вас есть значение f и вы хотите что-то узнать об этом, ваш единственный выбор - применить его к значению x, чтобы получить f x, что является другой функцией, и единственный выбор - применить его к другому значению y, чтобы получить f x y и так далее.

Часто я интерпретирую чистое лямбда-исчисление, говоря о преобразованиях в вещах, которые не являются функциями, но только способны выражать сами функции. То есть, я могу сделать функцию (с небольшим количеством синтаксического сахара Haskelly для рекурсии и let):

purePlus = \zero succ natCase -> 
   let plus = \m n -> natCase m n (\m' -> plus m' n)
   in plus (succ (succ zero)) (succ (succ zero))

Здесь я изложил вычисление 2+2, не зная, что существуют такие вещи, как не-функции. Я просто использовал то, что мне нужно, в качестве аргументов для функции, которую я определял, и значения этих аргументов могли быть кодовыми кодировками или они могли быть "реальными" числами (что бы это ни значило) - мое определение не заботится.

И вы могли бы думать то же самое о Haskell. Нет никаких оснований думать, что есть вещи, которые не являются функциями, и нет особой причины думать, что все является функцией. Но система типа Haskell, по крайней мере, мешает вам применять аргумент к числу (любой, кто думает о fromInteger, теперь должен держать свой язык!:-). В приведенной выше интерпретации это потому, что числа не обязательно моделируются как функции, поэтому вы не можете применять к ним аргументы.

В случае, если это пока не ясно, весь этот ответ был своего рода техническим/философским отступлением, и легкий ответ на ваш вопрос - "нет, не все функции на функциональных языках". Функции - это то, к чему вы можете применить аргументы, все.

Ответ 3

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

Любая программа, которая использует списки, например, может быть переведена в программу, которая использует функции вместо списков:

-- | A list corresponds to a function of this type:
type ChurchList a r = (a -> r -> r)  --^ how to handle a cons cell
                   -> r              --^ how to handle the empty list
                   -> r              --^ result of processing the list

listToCPS :: [a] -> ChurchList a r
listToCPS xs = \f z -> foldr f z xs

Эта функция принимает конкретный список в качестве отправной точки, но это необязательно. Вы можете создавать функции ChurchList только из чистых функций:

-- | The empty 'ChurchList'.
nil :: ChurchList a r
nil = \f z -> z

-- | Add an element at the front of a 'ChurchList'.
cons :: a -> ChurchList a r -> ChurchList a r
cons x xs = \f z -> f z (xs f z)

foldChurchList :: (a -> r -> r) -> r -> ChurchList a r -> r
foldChurchList f z xs = xs f z

mapChurchList :: (a -> b) -> ChurchList a r -> ChurchList b r
mapChurchList f = foldChurchList step nil
    where step x = cons (f x)

filterChurchList :: (a -> Bool) -> ChurchList a r -> ChurchList a r
filterChurchList pred = foldChurchList step nil
    where step x xs = if pred x then cons x xs else xs

Эта последняя функция использует Bool, но, конечно, мы можем заменить Bool на функции:

-- | A Bool can be represented as a function that chooses between two 
-- given alternatives.
type ChurchBool r = r -> r -> r

true, false :: ChurchBool r
true a _ = a
false _ b = b

filterChurchList' :: (a -> ChurchBool r) -> ChurchList a r -> ChurchList a r
filterChurchList' pred = foldChurchList step nil
    where step x xs = pred x (cons x xs) xs

Такое преобразование может быть выполнено для любого типа, поэтому теоретически вы можете избавиться от всех "значений" типов в Haskell и сохранить только тип (), тип (->) и IO конструкторы return и >>= для IO и подходящий набор примитивов IO. Это, очевидно, было бы нецелесообразным, и это будет хуже (попробуйте написать tailChurchList :: ChurchList a r -> ChurchList a r для вкуса).

Ответ 4

"Чистый" в "чистом функционале" относится к чистоте "свобода от побочных эффектов". Он мало связан со значением "чистого", используемого, когда люди говорят о "чисто объектно-ориентированном языке", что просто означает, что язык манипулирует чисто (только) объектами.

Причина в том, что pure-as-in-only является разумным различием, которое следует использовать для классификации объектно-ориентированных языков, поскольку существуют такие языки, как Java и С++, которые явно имеют ценности, которые не имеют всего такого общего с объектами, а также есть такие языки, как Python и Ruby, для которых можно утверждать, что каждое значение является объектом 1

В то время как для функциональных языков нет практических языков, которые являются "чисто функциональными" в том смысле, что каждое значение, которое может манипулировать языком, является функцией. Конечно, можно программировать на таком языке. Самые базовые версии лямбда-исчисления не имеют понятия о вещах, которые не являются функциями, но вы все же можете делать с ними произвольные вычисления, придумывая способы представления объектов, которые вы хотите вычислить как функции. 2

Но в то время как простота и минимализм лямбда-исчисления имеет большое значение для доказательства того, что касается программирования, на самом деле писать существенные программы на таком "необработанном" языке программирования неудобно. Функция представления основных вещей, таких как числа, также имеет тенденцию быть очень неэффективной для реализации на реальных физических машинах.

Но существует очень важное различие между языками, которые поощряют функциональный стиль, но позволяют избежать необратимых побочных эффектов в любом месте, и те, которые фактически обеспечивают, чтобы ваши функции были "чистыми" функциями (аналогичными математическим функциям). Объектно-ориентированное программирование очень сильно зависит от использования нечистых вычислений 3 поэтому нет практических объектно-ориентированных языков программирования, которые являются чистыми в этом смысле.

Таким образом, "чистый" в "чистом функциональном языке" означает нечто очень отличное от "чистого" в "чисто объектно-ориентированном языке". 4 В каждом случае "чистый vs not pure" различие - это то, что совершенно неинтересно относится к другому типу языка, поэтому нет особого мотива для стандартизации использования термина.


1 Есть угловые случаи для выбора на всех "чистых объектно-ориентированных" языках, о которых я знаю, но это не очень интересно. Ясно, что метафора объекта идет гораздо дальше на языках, в которых 1 является экземпляром некоторого класса, и этот класс может быть подклассифицирован, чем в языках, в которых 1 - это нечто иное, чем объект.

2 Все вычисления в любом случае имеют представление. Компьютеры ничего не знают о цифрах или что-то еще. Они просто имеют битовые шаблоны, которые мы используем для представления чисел, и операции над битовыми шаблонами, которые соответствуют операциям с числами (потому что мы их проектировали так, чтобы они были).

3 Это тоже не фундаментально. Вы могли бы создать "чистый" объектно-ориентированный язык, который был чистым в этом смысле. В любом случае, я предпочитаю писать большую часть моего кода OO.

4 Если это кажется тупым, вы можете подумать, что термины "функциональный", "объект" и "язык" имеют и другие значения в других контекстах.

Ответ 5

Является ли getChar :: IO Char функцией или нет? Отчет Haskell не дает нам определения. Но он утверждает, что getChar является функцией (см. здесь). (Ну, по крайней мере, мы можем сказать, что это функция.)

Итак, я думаю, что ДА.

Я не думаю, что может быть правильное определение "функции", кроме "все является функцией". (Что такое "правильное определение"? Хороший вопрос...) Рассмотрим следующий пример:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Applicative

f :: Applicative f => f Int
f = pure 1

g1 :: Maybe Int
g1 = f

g2 :: Int -> Int
g2 = f

Является ли f функцией или типом данных? Это зависит.