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

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

редактировать

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

7 ответов


я посмотрел на F#, делая уроки низкого уровня, поэтому мои знания об этом очень ограничены. Тем не менее, мне было очевидно, что его стиль был по существу функциональным, а OO был больше похож на add on-гораздо больше на систему ADT + module, чем true OO. Чувство, которое я получаю, можно лучше всего описать, как если бы все методы в нем были статическими (как в Java static).

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

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

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

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

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

List(1 to 10) map fib

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

здесь map метод на экземпляр List. Статические методы, такие как intWrapper on Predef или apply on 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 преобразует это в функцию, чтобы иметь возможность передать его map. Я изменю его ниже, чтобы он стал методом, который возвращает функцию, чтобы показать, как функции работают в 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 является объектом -- и экземпляром класс - и каждый такой объект принадлежит классу, а весь код принадлежит методу, который каким-то образом выполняется. Даже match в примере выше раньше метод, но был преобразован в Ключевое слово, чтобы избежать некоторых проблем довольно давно.

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

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

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

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

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

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

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

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

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

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

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

EDIT

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

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

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

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

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

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

Примечания:

  1. на самом деле алгоритм происходит от Дамаса и Милнера, которые назвали его "алгоритмом W", согласно Википедия.

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

    причина Scala не имеют Хиндли/Мильнер вывод типа что это очень трудно совместить с такими функциями, как перегрузка (вариант ad-hoc, а не классы типов), запись выделение и подтипирование

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


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

  • функции в стиле Haskell
  • автоматическое каррирование
  • автоматическая дженериков
  • вывод типов для Аргументов

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

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


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

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

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

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

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


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

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

Scala пришлось пойти на жертвы, чтобы объединить парадигмы. Для пример:

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

из приведенного отрывка ясно, что Подход Scala к объединению парадигм OO и FP намного превосходит подход OCaml или F#.

HTH.

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


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

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


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

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