2016-05-09 2 views
9

В ООП это хорошая практика говорить интерфейсам не к реализациям. Так, например, вы пишете что-то вроде этого (по Seq я имею в виду scala.collection.immutable.Seq :)):Несимение объектно-функционального импеданса

// talk to the interface - good OOP practice 
doSomething[A](xs: Seq[A]) = ??? 

не что-то вроде следующего:

// talk to the implementation - bad OOP practice 
doSomething[A](xs: List[A]) = ??? 

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

В гибридных языках, таких как Scala, у вас есть классы типа (через шаблон, фактически, а не первоклассные граждане, как в Haskell, но я отвлекаюсь) и полиморфизм подтипов. В scalaz, cats и т. Д. У вас есть монадические экземпляры для конкретных типов, а не абстрактные, конечно.

Наконец вопрос: при таком гибридность от Scala вы до сих пор уважают правила ООП говорить с интерфейсами или просто поговорить с конкретными типами, чтобы воспользоваться функторы, монады и так далее непосредственно без необходимости конвертировать в бетон типа, когда вам нужно их использовать? По-другому, в Скала по-прежнему хорошая практика разговаривать с интерфейсами, даже если вы хотите использовать FP вместо OOP? Если нет, что делать, если вы решили использовать List, и, позже, вы поняли, что выбор Vector был бы лучшим выбором?

P.S .: В моих примерах я использовал простой метод, но те же рассуждения применимы к пользовательским типам. Например:

case class Foo(bars: Seq[Bar], ...) 
+0

Существует также .. 'def doSomething [A, M [X] <: Seq [X]] (xs: M [A]) = ???' –

+1

Этот вопрос может быть лучше подходит для [Programmers SE] (http://programmers.stackexchange.com/). Основными причинами разговора с интерфейсами являются: 1) вы можете заменить реализацию другим и 2), чтобы вы могли изолировать свои типы друг от друга при написании модульных тестов. Эти проблемы не относятся к «примитивным» типам, таким как ints или string, для интерфейса 'int' или' string' нет интерфейса. – dcastro

+0

Я считаю, что большинство монадов являются примитивами, которые помогают склеить ваш код вместе. Поэтому нет необходимости в интерфейсах. – dcastro

ответ

1

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

Итак, давайте посмотрим на списки Haskell с этого угла. Каков интерфейс списка? Ну, списки являются алгебраический тип данных, и все такие типы данных имеют один и тот же общий вид интерфейса и договора:

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

Если вы посмотрите на него в этих условиях, я думаю, что следующее правило работает очень хорошо в любой парадигме:

  • Выберите типы, чьи интерфейсы и контракты соответствуют точно с вашими требованиями.
    • Если их контракт слабее ваших требований, то они не будут поддерживать инварианты, которые вам нужны;
    • Если их контракты сильнее ваших требований, вы можете непреднамеренно связать себя с «лишними» деталями и ограничить возможность изменения программы позже.

Таким образом, вы больше не спрашивать тип является ли «бетон» или «абстрактный» -Просто соответствует ли вашим требованиям.

+1

Это не таким образом, что работает в ООП. В Haskell у вас есть типы данных (ADT). У вас есть как списки (связанные списки), так и векторы (int-indexed массивы), но они не имеют общего _supertype_. Если ваша функция принимает список, вы не можете передать ему вектор. В Java/Scala у вас есть полиморфизм подтипа, и вам не нужно заботиться о том, проходит ли код клиента по списку или вектору, вы просто берете Seq (List in Java), и все готово. Кроме того, в ООП это хорошая практика, заключающаяся в Seq, а не в List или Vector, поскольку Seq соответствует именно вашему требованию, поэтому вам не нужно заботиться о конкретной реализации, прошедшей в. – lambdista

+0

Я бы сказал, что эквивалент интерфейсов oop в этом случае чтобы считаться стилем haskell. Рассматриваемая абстракция заключается в написании общего кода, который работает с минимально необходимым контрактом для ваших данных. –

+0

@lambdista: Существуют простые функции, которые конвертируют между списками и векторами. Поскольку списки Haskell ленивы, оперативно это очень похоже на итераторы/'Seq'. Итак, проблема решена. ООП слишком много разбирается в отношениях IS-A, когда CAN-BE-TURNED-INTO-A работает уже очень хорошо. –

0

Это мои два цента на эту тему. В Haskell у вас есть типы данных (ADT). У вас есть как списки (связанные списки), так и векторы (int-indexed массивы), но они не имеют общего супертипа. Если ваша функция принимает список, вы не можете передать ему вектор.

В Scala, будучи его гибридный ООП-FP языка, то есть подтип полиморфизм тоже, так что вы можете не заботиться, если код клиента пропускает List или Vector, просто требует Seq (возможно, неизменный), и вы сделали ,

Я думаю, что для ответа на этот вопрос вы должны задать себе другой вопрос: «Я хочу включить FP в toto?». Если да, то вы не должны использовать Seq или любые другие абстрактные суперклассы в смысле ООП. Конечно, исключение из этого правила заключается в использовании класса признаков/абстрактных при определении ADT в Scala. Например:

sealed trait Tree[+A] 
case object Empty extends Tree[Nothing] 
case class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] 

В этом случае можно было бы потребовать Tree[A] как тип, конечно, а затем использовать, например, сопоставление с образцом, чтобы определить, является ли это либо Empty или Node[A].

Я думаю, что мое чувство об этом предмете подтверждается красной книгой (Functional Programming in Scala). Там они никогда не используют Seq, но List, Vector и так далее. Кроме того, haskellers, не заботятся об этих проблемах и используют списки всякий раз, когда им нужна семантика и векторы связанных списков всякий раз, когда им нужна семантика int-indexed-array.

Если, с другой стороны, вы хотите, чтобы охватить ООП и использовать Scala как лучше Java затем OK, вы должны следовать передовой практике ООП для разговора для интерфейсов не реализации.

Если вы думаете: «Я предпочел бы выбрать в основном функциональный», тогда вы должны прочитать The Curse of the Excluded Middle Эрика Мейджера.

Смежные вопросы