Scala по сравнению с вопросом F#: как они объединяют парадигмы FP и OO?

Каковы основные отличия между подходами, проявленными Scala и F# для объединения парадигм FP и OO?

Править

Каковы относительные достоинства и недостатки каждого подхода? Если, несмотря на поддержку выделения подтипов, F# может вывести типы аргументов функции затем, почему не может Scala?

40
задан 6 revs, 3 users 100% 26 May 2010 в 09:16
поделиться

7 ответов

Я изучал F #, выполняя низкоуровневые учебные пособия, поэтому мои знания о нем очень ограничены. Однако мне было очевидно, что его стиль был в основном функциональным, а объектно-ориентированный подход был больше похож на надстройку - в большей степени на модульную систему ADT +, чем на настоящий объектно-ориентированный объект. Ощущение, которое я испытываю, лучше всего можно описать так, как будто все методы в нем были статическими (как в Java static).

См., Например, любой код, использующий оператор вертикальной черты ( |> ). Возьмите этот фрагмент из статьи википедии на F # :

[1 .. 10]
|> List.map     fib

(* equivalent without the pipe operator *)
List.map fib [1 .. 10]

Функция map не является методом экземпляра списка. Вместо этого он работает как статический метод в модуле List , который принимает экземпляр списка в качестве одного из своих параметров.

Scala, с другой стороны, полностью объектно-ориентированный. Начнем сначала с Scala-эквивалента этого кода:

List(1 to 10) map fib

// Without operator notation or implicits:
List.apply(Predef.intWrapper(1).to(10)).map(fib)

Здесь map - это метод экземпляра List . Статические методы, такие как intWrapper в Predef или apply в List , встречаются гораздо реже. Затем есть функции, такие как fib выше. Здесь fib - это не метод для int , но и статический метод. Вместо этого это объект - второе основное различие, которое я вижу между F # и Scala.

Давайте рассмотрим реализацию F # из Википедии и эквивалентную реализацию Scala:

// F#, from the wiki
let rec fib n =
    match n with
    | 0 | 1 -> n
    | _ -> fib (n - 1) + fib (n - 2)

// Scala equivalent
def fib(n: Int): Int = n match {
  case 0 | 1 => n
  case _ => fib(n - 1) + fib(n - 2)
}

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

// F#, returning a lambda, as suggested in the comments
let rec fib = function 
    | 0 | 1 as n -> n 
    | n -> fib (n - 1) + fib (n - 2)

// Scala method returning a function
def fib: Int => Int = {
  case n @ (0 | 1) => n
  case n => fib(n - 1) + fib(n - 2)
}

// Same thing without syntactic sugar:
def fib = new Function1[Int, Int] {
  def apply(param0: Int): Int = param0 match {
    case n @ (0 | 1) => n
    case n => fib.apply(n - 1) + fib.apply(n - 2)
  }
}

Итак, в Scala все функции являются объектами, реализующими трейт FunctionX , который определяет метод с именем apply . Как показано здесь и в приведенном выше создании списка, .apply можно опустить, что делает вызовы функций похожими на вызовы методов.

В конце концов, все в Scala является объектом - и экземпляром класса - и каждый такой объект действительно принадлежит классу, а весь код принадлежит методу, который каким-то образом выполняется. Даже соответствие в приведенном выше примере раньше было методом, но некоторое время назад было преобразовано в ключевое слово, чтобы избежать некоторых проблем.

А как насчет функциональной части? F # принадлежит к одному из самых традиционных семейств функциональных языков. Хотя в нем нет некоторых функций, которые, по мнению некоторых людей, важны для функциональных языков, факт в том, что F # работает по по умолчанию , так сказать.

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

  • Функции - это значения.Они тоже являются объектами - потому что все значения являются объектами в Scala, - но концепция, что функция - это значение, которым можно манипулировать, является важной, поскольку ее корни уходят в исходную реализацию Лиспа.

  • Сильная поддержка неизменяемых типов данных. Функциональное программирование всегда было связано с уменьшением побочных эффектов в программе, чтобы функции можно было анализировать как истинные математические функции. Итак, Scala упростила создание неизменяемых вещей, но не сделала двух вещей, за которые ее критикуют пуристы FP:

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

    Как и все остальное, ADT в Scala реализованы в виде классов и методов с некоторыми синтаксическими сахарами, которые делают их безболезненными в использовании. Однако Scala гораздо более подробна, чем F # (или другие функциональные языки, если на то пошло) в их поддержке. Например, вместо F # | для операторов case он использует case .

  • Поддержка нестрогости. Нестрогость означает выполнение вычислений только по запросу. Это важный аспект Haskell, где он тесно интегрирован с системой побочных эффектов. В Scala, однако, поддержка нестрогости довольно робкая и зарождающаяся.Он доступен и используется, но ограниченно.

    Например, нестрогий список Scala, Stream , не поддерживает действительно нестрогие foldRight , как это делает Haskell. Более того, некоторые преимущества нестрогости достигаются только тогда, когда это значение по умолчанию в языке, а не опция.

  • Поддержка понимания списка. На самом деле,Scala называет это для понимания , поскольку способ его реализации полностью отделен от списков. Проще говоря, составление списков можно рассматривать как функцию / метод map , показанные в примере, хотя вложение операторов карты (поддерживается с помощью flatMap в Scala), а также фильтрация ( filter или withFilter в Scala, в зависимости от требований строгости) обычно ожидаются.

    Это очень распространенная операция в функциональных языках, и ее синтаксис часто невелик - как в операторе Python in . Опять же, Scala более подробна, чем обычно.

На мой взгляд, Scala не имеет себе равных в сочетании FP и OO. Он идет от стороны OO спектра к стороне FP, что необычно. Чаще всего я вижу языки программирования FP с ОО-объектами - и мне кажется, что занялся этим. Я полагаю, что FP на Scala, вероятно, чувствует то же самое для программистов на функциональных языках.

РЕДАКТИРОВАТЬ

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

Scala не может использовать алгоритм Хиндли-Милнера из-за поддержки Scala наследования 2 .Таким образом, Scala должен принять гораздо менее мощный алгоритм вывода типов - на самом деле, вывод типов в Scala намеренно не определен в спецификации и подлежит постоянным улучшениям (это улучшение является одной из важнейших особенностей грядущей версии 2.8. Скала, например).

В конце концов, однако, Scala требует, чтобы все параметры объявляли их типы при определении методов. В некоторых ситуациях, таких как рекурсия, также необходимо объявить возвращаемые типы для методов.

Однако для функций в Scala часто могут быть выведены типы , а не объявлены. Например, здесь объявление типа не требуется: List (1, 2, 3) reduceLeft (_ + _) , где _ + _ на самом деле является анонимной функцией типа ] Функция2 [Int, Int, Int] .

Точно так же объявление типа переменных часто не требуется, но наследование может потребовать этого. Например, Some (2) и None имеют общий суперкласс Option , но фактически принадлежат к разным подклассам. Поэтому обычно объявляют var o: Option [Int] = None , чтобы убедиться, что назначен правильный тип.

Эта ограниченная форма вывода типов намного лучше, чем обычно предлагают статически типизированные объектно-ориентированные языки, что придает Scala ощущение легкости, и намного хуже, чем обычно предлагают статически типизированные языки программирования FP, что придает Scala ощущение тяжести. : -)

Примечания:

  1. На самом деле алгоритм происходит от Дамаса и Милнера, которые, согласно википедии , назвали его «Алгоритм W».

  2. Мартин Одерски упомянул в комментарии здесь , что:

    Причина, по которой в Scala нет вывода типа Хиндли / Милнера, является что очень сложно сочетать с такими функциями, как перегрузка (специальный вариант, а не классы типов), запись отбор и подтипирование

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

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

43
ответ дан 27 November 2019 в 01:20
поделиться

F # является функциональным - Он позволяет OO довольно хорошо, но дизайн и философия, тем не менее, функциональны. Примеры:

  • Функции в стиле Haskell
  • Автоматическое каррирование
  • Автоматические обобщения
  • Выведение типа для аргументов

Использование F # в основном объектно-ориентированным способом кажется относительно неуклюжим. цель интегрировать объектно-ориентированный объект в функциональное программирование .

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

14
ответ дан 27 November 2019 в 01:20
поделиться

Есть довольно много точек, которые вы можете использовать для сравнения двух (или три). Во-первых, вот некоторые примечательные моменты, о которых я могу думать:

  • Синтаксис
    Синтаксически, F # и OCaml основаны на традиции функционального программирования (разделены пробелами и более легкие ), тогда как Scala основан на объектно-ориентированном стиле (хотя Scala делает его более легким).

  • Интеграция OO и FP
    Оба F # и Scala очень плавно интегрируют OO с FP (потому что между этими двумя нет противоречия !!) Вы можете объявить классы как выполняемые неизменяемые данные (функциональный аспект) и предоставляют члены, связанные с работой с данными, вы также можете использовать интерфейсы для абстракции (объектно-ориентированные аспекты).Я не так хорошо знаком с OCaml , но я думаю, что он уделяет больше внимания объектно-ориентированной стороне (по сравнению с F #)

  • Стиль программирования в F #
    Я думаю, что обычный стиль программирования используемый в F # (если вам не нужно писать библиотеку .NET и у вас нет других ограничений), вероятно, более функциональный, и вы будете использовать объектно-ориентированные функции только тогда, когда вам нужно. Это означает, что вы группируете функциональные возможности, используя функции, модули и алгебраические типы данных.

  • Стиль программирования в Scala
    В Scala стиль программирования по умолчанию более объектно-ориентирован (в организации), однако вы все еще (вероятно) пишете функциональные программы, потому что «стандартный» подход заключается в написании кода, который избегает мутация.

13
ответ дан 27 November 2019 в 01:20
поделиться

Если функциональное программирование означает программирование с помощью функций , то Scala немного изменит это. На Scala, если я правильно понимаю, вы программируете с помощью методов вместо функций.

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

3
ответ дан 27 November 2019 в 01:20
поделиться

Синтаксис F # был взят из OCaml, но объектная модель F # была взята из .NET. Это дает F # легкий и краткий синтаксис, характерный для функциональных языков программирования, и в то же время позволяет F # очень гладко взаимодействовать с существующими языками .NET и библиотеками .NET через его объектную модель.

Scala выполняет ту же работу в JVM, что и F # в CLR. Однако Scala выбрала синтаксис, более похожий на Java. Это может помочь в его принятии объектно-ориентированными программистами, но для функционального программиста это может показаться немного тяжелым. Его объектная модель похожа на модель Java, допускающую бесшовное взаимодействие с Java, но имеет некоторые интересные отличия, такие как поддержка трейтов.

5
ответ дан 27 November 2019 в 01:20
поделиться

Из этой статьи о языках программирования:

Scala - это прочный, выразительный, строго улучшенная замена Джава. Scala - это программирование язык, который я бы использовал для такой задачи, как написание веб-сервера или IRC-клиента. В отличие от OCaml [или F #], который был функциональный язык с привитая объектно-ориентированная система, Scala больше похож на настоящий гибрид объектно-ориентированных и функциональных программирование. (то есть объектно-ориентированный программисты должны уметь запускать немедленно использовать Scala, подбирая функциональные части только как они выберите.)

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

Потребность в Scala отпала. очевидно для меня, пока я не написал одновременный HTTPD с нуля на поддержка долгого опроса AJAX для яплета. Чтобы получить хорошую многоядерность поддержку, первую версию я написал в Джава. Как язык я не думаю Java - это все так плохо, и я могу наслаждаться хорошо сделанное объектно-ориентированное программирование. Однако как функциональный программист отсутствие (или излишне многословный) поддержка функционального программирования функции (например, функции высшего порядка) меня раздражает, когда я программирую на Java. Итак, я дал Scala шанс.

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

Когда я начал использовать Scala, я стал впечатлен тем, насколько умно функциональные и объектно-ориентированные миры смешанные вместе. В частности, Scala имеет веское дело система сопоставления классов / шаблонов, которая обратился к животным мозолям, оставшимся от моего опыт работы со Standard ML, OCaml и Haskell: программист может решить какие поля объекта должны быть совместимый (в отличие от принудительного чтобы соответствовать всем из них), и аргументы переменной арности разрешенный. Фактически, Scala даже позволяет шаблоны, определяемые программистом. Я пишу множество функций, которые работают на абстрактные синтаксические узлы, и это приятно чтобы иметь возможность сопоставить только синтаксические дети, но все еще есть поля для таких вещей, как аннотации или строки в исходной программе. система классов случаев позволяет разделить определение алгебраического типа данных через несколько файлов или через несколько частей одного файла, которые замечательно удобно.

Scala также поддерживает четко определенные множественные наследование через классовые устройства называется чертами.

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

Одна функция, которая помогает сохранить много кода, точно так же, как этот тип классы сохраняют код в Haskell, это имплицит. Вы можете себе представить последствия как API для фазы исправления ошибок тип-чекера. Короче, когда Средству проверки типов нужен X, но есть Y, он проверит, есть ли неявная функция в области видимости, которая конвертирует Y в X; если найдет, то "приведения" с использованием неявного. Это делает можно выглядеть как ты расширение практически любого типа в Scala, и это позволяет вложения DSL.

Из приведенного выше отрывка ясно, что подход Scala к унификации парадигм объектно-ориентированного программирования и FP намного превосходит подход OCaml или F #.

HTH.

С уважением,
Эрик.

7
ответ дан 27 November 2019 в 01:20
поделиться

Каковы ключевые различия между подходами Scala и F# к объединению парадигм OO и FP?

Ключевое различие в том, что Scala пытается объединить парадигмы, идя на жертвы (обычно со стороны FP), тогда как F# (и OCaml) обычно проводит границу между парадигмами и позволяет программисту выбирать между ними для каждой задачи.

Scala пришлось пойти на жертвы, чтобы унифицировать парадигмы. Например:

  • Функции первого класса - неотъемлемая черта любого функционального языка (ML, Scheme и Haskell). В F# все функции являются первоклассными. В Scala функции-члены являются функциями второго класса.

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

Другим следствием этого является то, что F# основан на проверенных и испытанных идеях, в то время как Scala является новатором в этом отношении. Это идеально подходит для мотивации проектов: F# - коммерческий продукт, а Scala - исследование языка программирования.

В качестве отступления, Scala также пожертвовала другими ключевыми особенностями FP, такими как оптимизация хвостовых вызовов, по прагматическим причинам из-за ограничений выбранной ими виртуальной машины (JVM). Это также делает Scala гораздо более ООП, чем FP. Обратите внимание, что существует проект по переносу Scala в .NET, который будет использовать CLR для выполнения подлинного ООП.

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

Вывод типов противоречит ОО-ориентированным особенностям, таким как перегрузка и подтипы. F# выбрал вывод типов вместо согласованности в отношении перегрузки. Scala выбрала повсеместную перегрузку и подтипы, а не вывод типов. Это делает F# более похожим на OCaml, а Scala - на C#. В частности, Scala не более функциональный язык программирования, чем C#.

Что лучше, конечно, совершенно субъективно, но лично я предпочитаю огромную краткость и ясность, которая приходит от мощного вывода типов в общем случае. OCaml - замечательный язык, но одной из его болевых точек было отсутствие перегрузки операторов, из-за чего программистам приходилось использовать + для интов, +. для плавающих чисел, +/ для рациональных чисел и так далее. И снова F# выбирает прагматизм вместо одержимости, жертвуя выводом типов ради перегрузки именно в контексте чисел, не только для арифметических операторов, но и для арифметических функций, таких как sin. Каждый уголок языка F# является результатом тщательно выбранных прагматических компромиссов, подобных этому. Несмотря на возникающие несоответствия, я считаю, что это делает F# гораздо более полезным.

13
ответ дан 27 November 2019 в 01:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: