F # - это первый функциональный язык, но, будучи многопарадигмальным, он может обрабатывать классы, объекты, наследование, методы и т. Д. Поскольку многие из моих друзей привыкли к таким языкам, как Java или C # (или даже C ++), возникает вопрос: вы можете делать объектную ориентацию с F # возникает часто, настолько, что я попытаюсь показать вам несколько примеров того, как это сделать. Я должен сразу сказать, что они будут (очень) упрощенными примерами, но я надеюсь, что они дадут вам реалистичное представление о том, как писать O-O F # в любом случае. Я собираюсь использовать типичный сценарий класса Vehicle, от которого наследуется класс Car. Для начала давайте посмотрим, как определить цвет как перечисление (мы используем перечисления, верно?) И положение как неизменяемый объект (конкретные координаты (0,24, -78,5) не меняются, если транспортное средство перемещается, его положение явно изменяется, но он делает это, получая новые координаты). Версия Java:

Кто-то может сказать, что долгота и широта должны иметь геттеры, но поскольку экземпляр Position не изменяется (отсюда final в объявлениях полей), определение и использование геттера кажется излишним, и я не хочу, чтобы Java выглядела длиннее, чем необходимо. Теперь версия F #:

Синтаксис enum отличается, но эквивалентность очевидна. Position действительно приносит некоторые новые идеи: мы видим объявление record, простой тип объекта, который имеет только свойства, которые также являются неизменяемыми (например, final fields), это может выглядеть как очень узкий случай, но поскольку F # предпочитает использование неизменяемых элементов, это происходит гораздо чаще, чем в обычном объектно-ориентированном коде.

Теперь перейдем к чему-то более содержательному - приношу извинения моим друзьям-веганам :-) Класс Vehicle, который является абстрактным, имеет несколько полей, конструктор и метод. Версия Java:

Как обычно, поля Color, Position и Speed ​​ вместе с их объявлениями getter и setter занимают большую часть места; конструктор инициализирует пару из этих полей через соответствующие сеттеры, как рекомендовано в Java; наконец, метод Accelerate увеличивает скорость и возвращает новое значение. Конечно, мы могли бы, и должны были бы иметь гораздо больше логики, но это не является целью данной статьи. Теперь версия F #:

Прямо в первой строке довольно странно использование атрибута AbstractClass для обозначения того, что мы имеем дело с абстрактным классом (эй! Никто не сказал, что здесь все будет очевидно: - )), на этом давайте разберемся с более важными деталями:

  1. Скобки сразу после имени класса указывают на то, что конструктор по умолчанию не имеет аргументов (ага, да, эти «()» являются нашим объявлением конструктора Vehicle () ;-))
  2. Color, Position и Speed ​​ - это свойства с невидимыми полями позади них, с автоматически сгенерированными геттерами и сеттерами, аналогичными тем, что в версии для Java.
  3. Это {длина = 0,0; latitude = 0,0} создает экземпляр объекта Position, по сути, это эквивалент new Position (0, 0)
  4. Метод Accelerate имеет один аргумент, скобки вокруг него не нужны, ключевое слово return также не требуется: самое последнее вычисленное выражение, в данном случае this.Speed ​​, возвращается
  5. Наконец, нет необходимости в объявлении какого-либо типа, все эти Color, Position, float- ›float и тому подобное, выделенные светло-серым цветом, визуальные подсказки, созданные компилятором, ничего из того, что мне нужно было вводить.

В общем, мы говорим о 32 строках Java и 8 строках F # , чтобы выразить одно и то же. 75% экономии строк для чтения и понимания - это неплохо, не так ли? :-)

Мы почти закончили, Car - это класс, унаследованный от Vehicle, который добавляет поле, неизменяемое, чтобы проиллюстрировать, как это делается, и метод. Версия Java:

Бренд - это свойство, инициализируемое внутри конструктора, которое впоследствии не изменяется; конструктор получает два параметра: brand, которое сопоставляется с локальным полем, и color, которое сопоставляется с полем предка через его установщик; Наконец, метод PullHandBrake замедляет автомобиль до 0 (я сказал, что это будет упрощенный пример, верно? :-)) с использованием методов и геттеров предка, ничего особенного в этом нет. . Теперь версия F #:

Автомобиль происходит от Автомобиль, синтаксис очевиден, но опять же у нас есть некоторые интересные детали:

  1. () после Vehicle сигнализирует, какой конструктор-предок будет вызываться как часть экземпляра объекта Car, поэтому он вызывает тот же эффект, что и Java super () вызов
  2. Сам конструктор Car принимает два параметра, которые используются в двух разных местах: a) внутри оператора do, где размещается логика конструктора по умолчанию; б) При инициализации свойства Brand Brand: string = brand, которое, кстати, не имеет установщика, что делает его неизменяемым.
  3. Значение, возвращаемое PullHandBrake, такое же, как в Java, но мне нравится думать, что здесь меньше шума :-)

Здесь мы сэкономили всего 50% строк кода, но все же не так уж плохо, а?

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

Обновление 1. Вы можете получить образцы кода F # и Java здесь.

Обновление 2: Присоединяйтесь к функциональному движению!