Что составляет ценность в чисто функциональном программировании?
Фон
В чистом функциональном программировании нет мутаций.Следовательно, код, такой как
case class C(x: Int)
val a = C(42)
val b = C(42)
, станет эквивалентным
case class C(x: Int)
val a = C(42)
val b = a
, поскольку в pure функциональном программировании, если a.x == b.x
, то мы будем иметь a == b
.Таким образом, a == b
будет реализован путем сравнения значений внутри.
Однако Scala не является чистым, поскольку он допускает мутации, как в Java.В таком случае у нас НЕ будет эквивалентности между двумя фрагментами выше, когда мы объявляем case class C(var x: Int)
.Действительно, выполнение a.x += 1
послесловий не влияет на b.x
в первом фрагменте, но влияет на второй, где a
и b
указывают на один и тот же объект.В таком случае полезно иметь a == b
сравнение, которое сравнивает объект ссылки , а не его внутреннее целочисленное значение.
При использовании case class C(x: Int)
, сравнения Scala a == b
вести себя ближе к чисто функциональному программированию, сравнивая целые значения.С обычными (не case
) классами Scala вместо этого сравнивает ссылки на объекты, нарушая эквивалентность между двумя фрагментами.Но, опять же, Скала не чиста.Для сравнения, в Haskell
data C = C Int deriving (Eq)
a = C 42
b = C 42
действительно эквивалентен
data C = C Int deriving (Eq)
a = C 42
b = a
, поскольку в Haskell нет «ссылок» или «идентификаторов объектов».Обратите внимание, что реализация Haskell, скорее всего, выделит два «объекта» в первом фрагменте и только один объект во втором, но, поскольку в Haskell невозможно различить их, выходные данные программы будут одинаковыми.
Ответ
Является ли функция значением?(то, что это означает, приравнивая две функции: assert (f == g). Для двух функций, которые эквивалентны, но определены отдельно => f! = g, почему бы им не работать как 1 == 1)
Да, функции - это значения в чисто функциональном программировании.
Выше, когда вы упоминаете «функцию, которая эквивалентна, но определяется отдельно», вы предполагаете, что мы можем сравнить «ссылки» или «идентификаторы объектов»для этих двух функций.В чисто функциональном программировании мы не можем.
Чистое функциональное программирование должно сравнивать функции, делая f == g
эквивалентным f x == g x
для всех возможных аргументов x
.Это возможно, когда есть только несколько значений для x
, например, если f,g :: Bool -> Int
, нам нужно только проверить x=True, x=False
.Для функций, имеющих бесконечные области, это намного сложнее.Например, если f,g :: String -> Int
мы не можем проверить бесконечно много строк.
Теоретическая информатика (теория вычислимости) также доказала, что не существует алгоритма для сравнения двух функций String -> Int
, даже неэффективного алгоритма, недаже если у нас есть доступ к исходному коду двух функций.По этой математической причине мы должны признать, что функции - это значения, которые нельзя сравнивать.В Haskell мы выражаем это через класс типов Eq
, заявляя, что почти все стандартные типы сопоставимы, за исключением функций.
Является ли объект с методами значением?(например, IO {println ("")})
Да.Грубо говоря, «все является значением», включая действия ввода-вывода.
Является ли объект с методами установки и изменяемыми состояниями значением?Является ли объект с изменяемыми состояниями и работает как конечный автомат как значение?
В чистом функциональном программировании нет изменяемого состояния.
В лучшем случае, сеттеры могут создать «новое"объект с измененными полями.
И да, объект будет значением.
Как мы можем проверить, является ли это значением, то, что неизменяемость может быть достаточным условиембыть значением?
В чисто функциональном программировании мы можем иметь только неизменные данные.
В нечистом функциональном программировании я думаю, что мы можем назвать большинство неизменяемых объектов «значениями», когдамы не сравниваем ссылки на объекты.Если «неизменяемый» объект содержит ссылку на изменяемый объект, например,
case class D(var x: Int)
case class C(c: C)
val a = C(D(42))
тогда все сложнее.Я думаю, мы все еще можем назвать a
«неизменяемым», поскольку мы не можем изменить a.c
, но мы должны быть осторожны, так как a.c.x
может быть видоизменен.В зависимости от намерения, я думаю, что некоторые не будут называть a
неизменным.Я бы не считал a
значением.
Чтобы сделать вещи более грязными, в нечистом программировании есть объекты, которые используют мутации для эффективного представления "чистого" интерфейса.Например, можно написать чистую функцию, которая перед возвратом сохраняет свой результат в кеше.При повторном вызове с тем же аргументом он вернет ранее вычисленный результат (обычно это называется memoization ).Здесь происходит мутация, но она не наблюдается снаружи, где мы можем наблюдать более быструю реализацию.В этом случае мы можем просто притвориться, что эта функция является чистой (даже если она выполняет мутацию) и считать ее «значением».