Что такое «ценность» в чисто функциональном программировании? - PullRequest
0 голосов
/ 20 мая 2018

Что составляет ценность в чисто функциональном программировании?

Я задаю себе эти вопросы после просмотра предложения:

Task (или IO) имеет конструктор, который фиксирует побочные эффекты в виде значений .

  • Является ли функция значением?
    • Если это так, что это означает, приравнивая две функции: assert(f == g).Для двух функций, которые эквивалентны, но определены отдельно => f != g, почему они не работают как 1 == 1?
  • Является ли объект с методами значением?(например IO { println("") })
  • Является ли объект с методами установки и изменяемым состоянием значением?
  • Является ли объект с изменяемым состоянием, которое работает как конечный автомат, значением?

Как мы можем проверить, является ли что-то значением?Является ли неизменность достаточным условием?

ОБНОВЛЕНИЕ: Я использую Scala.

Ответы [ 5 ]

0 голосов
/ 27 мая 2018

Значения - это вещи, которые функции

  • могут принимать в качестве входных данных и возвращать в качестве выходных данных, то есть могут быть вычислены, а
  • являются членами типа, то есть элементаминекоторого набора, и
  • может быть привязан к переменной, то есть может иметь имя.

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

IO Монада является отличным примером того, как общиеэта концепция есть.Когда мы говорим IO, что монада моделирует побочные эффекты в качестве значений, мы подразумеваем, что функция может принимать побочный эффект (скажем, println) в качестве входных данных и возвращать в качестве выходных.IO(println(...)) отделяет идею эффекта действия println от фактического выполнения действия и позволяет рассматривать эти эффекты как значения первого класса, которые можно вычислить с использованием того же языкаобъекты, как и для любых других значений, таких как числа.

0 голосов
/ 21 мая 2018

Значения

  1. Неизменные / Вневременные
  2. Аноним
  3. Семантически прозрачные

Что такое значение 42?42. Что такое «ценность» new Date()?Date object at 0x3fa89c3.Какая личность 42?42. Что такое личность new Date()?Как мы видели в предыдущем примере, это то, что живет на месте.Он может иметь много разных «ценностей» в разных контекстах, но у него только одна идентичность.OTOH, 42 достаточно для себя.Семантически бессмысленно спрашивать, где 42 живет в системе.В чем смысл семантического значения 42?Величина 42. Каково семантическое значение new Foo()?Кто знает.

Я бы добавил четвертый критерий (см. Это в некоторых контекстах в дикой природе, но не в других), а именно: значения независимы от языка (я не уверен, что первые 3этого достаточно, чтобы гарантировать это или то, что такое правило полностью согласуется с интуицией большинства людей о том, что означает ценность).

0 голосов
/ 20 мая 2018

Контраст с императивными языками очевиден.В неинформативных языках, таких как Python, вывод функции направлен.Его можно присвоить переменной, явно вернуть, распечатать или записать в файл.

Когда я сочиняю функцию в Haskell, я никогда не рассматриваю вывод.Я никогда не использую «возврат». Все имеет значение «а».Это называется «символическим» программированием.Под «всем» подразумеваются «символы».Как и человеческий язык, существительные и глаголы представляют что-то.Это что-то их ценность.«Ценность» Пита - это Пит.Имя «Пит» - это не Пит, а представление о Пите, человеке.То же самое относится и к функциональному программированию.Лучшая аналогия - математика или логика. Когда вы выполняете страницы вычислений, направляете ли вы вывод каждой функции?Вы даже «назначаете» переменные для замены их «значением» в функциях или выражениях.

0 голосов
/ 20 мая 2018

Я попытаюсь объяснить, что такое значение , противопоставив его вещам, которые не являются значениями .

Грубо говоря, значения являются структурами, произведенными в процессе оценки , которые соответствуют терминам , которые не могут быть упрощены в дальнейшем.


условия

Во-первых, каковы термины ?Термины - это синтаксические структуры, которые могут быть оценены.Следует признать, что это немного округло, поэтому давайте рассмотрим несколько примеров:

  1. Постоянные литералы - это термины:

    42
    
  2. Применяемые функциик другим терминам относятся термины:

    atan2(123, 456 + 789)
    
  3. Литералы функций являются терминами

    (x: Int) => x * x
    
  4. Вызовы конструктора являются терминами:

    Option(42)
    

Сравните это с:

  1. Объявления / определения классов не являются терминами:

    case class Foo(bar: Int)
    

    , то есть вы не можете писать

    val x = (case class Foo(bar: Int))
    

    это было бы незаконно.

  2. Аналогично, определения признаков и типов не являются терминами:

    type Bar = Int
    sealed trait Baz
    
  3. В отличие от литералов функций, определения методов не являются терминами:

    def foo(x: Int) = x * x
    

    , например:

    val x = (a: Int) => a * 2 // function literal, ok
    val y = (def foo(a: Int): Int = a * 2) // no, not a term
    
  4. Объявления пакетов и операторы импорта не являются терминами:

    import foo.bar.baz._ // ok
    List(package foo, import bar) // no
    

Нормальные формы, значения

Теперь, когда, надеюсь, будет несколько яснее, что такое термин , чтоимел в виду "канноне быть упрощен дальше?В идеализированных функциональных языках программирования вы можете определить, что такое нормальная форма или, скорее, нормальная форма слабой головы .По сути, термин находится в (белой) нормальной форме, если к термину нельзя применить правила сокращения, чтобы упростить его.Опять же, несколько примеров:

  1. Это термин, но он не в нормальной форме, потому что его можно уменьшить до 42:

    40 + 2
    
  2. Это не в нормальной форме слабой головы:

    ((x: Int) => x * 2)(3)
    

    , потому что мы можем далее оценить его до 6.

  3. Эта лямбда находится внормальная форма слабой головы (она застряла, потому что вычисление не может продолжаться, пока не будет введено x):

    (x: Int) => x * 42
    
  4. Это не в нормальной форме, потому что это может быть упрощено в дальнейшем:

    42 :: List(10 + 20, 20 + 30)
    
  5. Это в нормальной форме, дальнейшие упрощения невозможны:

    List(42, 30, 50)
    

Таким образом,

  • 42,
  • (x: Int) => x * 42,
  • List(42, 30, 50)

являются значениями, тогда как

  • 40 + 2,
  • ((x: Int) => x * 2)(3),
  • 42 :: List(10 + 20, 20 + 30)

- не значения, а просто ненормализованные термины, которые могут быть дополнительно упрощены.


Примеры и не примеры

Я просто пойдуХотя ваш список подвопросов один за другим:

Является ли функция значением

Да, такие вещи, как (x: T1, ..., xn: Tn) => body, считаются застрявшими терминами вWHNF, в функциональных языках они действительно могут быть представлены, поэтому они являются значениями.

Если это так, что это значит, приравнивая две функции: assert(f == g) для двух функций, которые эквивалентны, но определены отдельно => f != g, почему они не работают как 1 == 1?

Расширение функций несколько не связано с вопросом, является ли что-то значением или нет.В приведенном выше «определении на примере» я говорил только о форме терминов, а не о существовании / несуществовании некоторых вычислимых отношений, определенных на этих терминах.Печальный факт заключается в том, что вы даже не можете реально определить, представляет ли лямбда-выражение функцию (т. Е. Завершается ли она для всех входных данных), и также известно, что не может быть алгоритма, который мог бы определить, производят ли две функцииодин и тот же выход для всех входов (т. е. экстенсивно равен).

Является ли объект с методами значением?(например IO { println("") })

Не совсем понятно, о чем вы здесь спрашиваете.У объектов нет методов.У классов есть методы.Если вы имеете в виду вызовы методов, то нет, это термины, которые можно еще более упростить (фактически запустив метод), поэтому они не являются значениями.

Является ли объект с методами установки и изменяемым состояниемценность?Является ли объект с изменяемым состоянием, который работает как конечный автомат, значением?

В чистом функциональном программировании такого нет.

0 голосов
/ 20 мая 2018

Что составляет ценность в чисто функциональном программировании?

Фон

В чистом функциональном программировании нет мутаций.Следовательно, код, такой как

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 ).Здесь происходит мутация, но она не наблюдается снаружи, где мы можем наблюдать более быструю реализацию.В этом случае мы можем просто притвориться, что эта функция является чистой (даже если она выполняет мутацию) и считать ее «значением».

...