[Перевод] Зачем выбирать F#?

02e6de0b2b8253e4777badc10884a706.png

Если бы кто-нибудь сказал мне несколько месяцев назад, что я буду снова экспериментировать с .NET после более чем пятнадцатилетней паузы, то я бы, наверно, рассмеялся1. В начале своей карьеры я пробовал работать с .NET и Java, и хотя некоторые вещи .NET делал лучше, чем Java (у него была возможность научиться на ошибках ранней Java), я быстро остановился на Java, потому что это была по-настоящему портируемая среда.

Наверно, читающие мой блог знают, что последние несколько лет я время от времени экспериментировал с OCaml, и я могу с уверенностью сказать, что он стал одним из моих любимых языков программирования наряду с Ruby и Clojure. Недавно работа с OCaml привлекла моё внимание к F# — это разработанный компанией Microsoft ML (Meta Language) для .NET, функциональная копия объектно-ориентированного (по большей мере) C#. Самый новый ML-язык…

Что такое F#?

К сожалению, никому нельзя объяснить, что такое Матрица. Ты должен увидеть её сам.

— Морфеус, «Матрица»

Прежде, чем мы перейдём к обсуждению F#, наверно, мне нужно сначала ответить на вопрос, что же такое F#. Для ответа на него я частично процитирую официальную страницу.

F# — универсальный язык программирования для написания краткого, надёжного и высокопроизводительного кода.

F# позволяет писать чёткий самодокументирующийся код, уделяя внимание предметной области задачи, а не деталям программирования.

При этом он не жертвует скоростью и совместимостью — он кроссплатформенный, опенсорсный и функционально совместимый.

open System // Получаем доступ к функциональности в пространстве имён System.

// Определяем список имён
let names = [ "Peter"; "Julia"; "Xi" ]

// Определяем функцию, получающую имя и создающую приветствие.
let getGreeting name = $"Hello, {name}"

// Выводим приветствие для каждого имени!
names
|> List.map getGreeting
|> List.iter (fun greeting -> printfn $"{greeting}! Enjoy your F#")

Любопытный факт:  F# — это язык, сделавший популярным оператор конвейера (|>).

F# обладает множеством особенностей, в том числе:

  • Легковесный синтаксис

  • Иммутабельность по умолчанию

  • Вывод типов и автоматическое обобщение

  • Функции первого класса

  • Мощные типы данных

  • Сопоставление паттернов

  • Асинхронное программирование

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

Выглядит многообещающе, правда?

F# 1.0 был официально выпущен Microsoft Research в мае 2005 года. Изначально он разрабатывался Доном Саймом из Microsoft Research в Кембридже, развившись из раннего исследовательского проекта Caml.NET, нацеленного на перенос OCaml на платформу .NET2. В 2010 году F# официально совершил переход из Microsoft Research в Microsoft (как часть отдела инструментария для разработчиков), что совпало по времени с релизом F# 2.0.

С тех пор F# стабильно развивался, а самый новый релиз F# 9.0 был выпущен в ноябре 2024 года. F# попал в зону моего внимания в год своего двадцатилетия!

Я хотел попробовать F# по множеству причин:

  • Несколько лет назад .NET стал опенсорсным и портируемым, и мне хотелось оценить прогресс на этом фронте

  • Мне было любопытно, есть ли у F# какие-то преимущества перед OCaml

  • Я слышал хорошие отзывы об инструментарии F# (например, Rider и Ionide)

  • Я люблю играться с новыми языками программирования

Ниже я расскажу о своих первых впечатлениях в разных областях.

Язык

Это член семейства ML-языков, поэтому его синтаксис не удивит людей, знакомых с OCaml (как будто таких людей много…). Стоит отметить, что программистам на Haskell синтаксис тоже будет родным. Как и программистам на Lisp.

А всем остальным будет достаточно просто освоить базу.

// применение функции
printfn "Hello, World!"

// определение функции
let greet name =
    printfn "Hello, %s!" name

greet "World"

// пробелы важны, как и в Python
let foo =
    let i, j, k = (1, 2, 3)

    // выражение:
    i + 2 * j + 3 * k

// условные выражения
let test x y =
  if x = y then "equals"
  elif x < y then "is less than"
  else "is greater than"

printfn "%d %s %d." 10 (test 10 20) 20

// Циклический обход списка
let list1 = [ 1; 5; 100; 450; 788 ]
for i in list1 do
   printfn "%d" i

// Обход последовательности кортежей
let seq1 = seq { for i in 1 .. 10 -> (i, i*i) }
for (a, asqr) in seq1 do
  printfn "%d squared is %d" a asqr

// Простой цикл for...to.
let function1 () =
  for i = 1 to 10 do
    printf "%d " i
  printfn ""

// Цикл for...to с обратным отсчётом
let function2 () =
  for i = 10 downto 1 do
    printf "%d " i
  printfn ""

// Записи

// Если метки определяются в одной строке, то они разделяются точкой с запятой
type Point = { X: float; Y: float; Z: float }

// Можно определять метки на отдельных строках без точки с запятой.
type Customer =
    { First: string
      Last: string
      SSN: uint32
      AccountNumber: uint32 }

let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }

// Дизъюнктное объединение
type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float

// Функция с сопоставлением паттернов
let area shape =
    match shape with
    | Circle radius -> System.Math.PI * radius * radius
    | Rectangle (width, height) -> width * height

let circle = Circle 5.0
let rectangle = Rectangle(4.0, 3.0)

printfn "Circle area: %f" (area circle)
printfn "Rectangle area: %f" (area rectangle)

Вроде бы, ничего неожиданного?

А вот ещё один пример, чуть более сложный:

open System

// Сэмплируем данные - простые записи о продажах
type SalesRecord = { Date: DateTime; Product: string; Amount: decimal; Region: string }

// Датасет
let sales = [
    { Date = DateTime(2023, 1, 15); Product = "Laptop"; Amount = 1200m; Region = "North" }
    { Date = DateTime(2023, 2, 3);  Product = "Phone";  Amount = 800m;  Region = "South" }
    { Date = DateTime(2023, 1, 20); Product = "Tablet"; Amount = 400m;  Region = "North" }
    { Date = DateTime(2023, 2, 18); Product = "Laptop"; Amount = 1250m; Region = "East" }
    { Date = DateTime(2023, 1, 5);  Product = "Phone";  Amount = 750m;  Region = "West" }
    { Date = DateTime(2023, 2, 12); Product = "Tablet"; Amount = 450m;  Region = "North" }
    { Date = DateTime(2023, 1, 28); Product = "Laptop"; Amount = 1150m; Region = "South" }
]

// Конвейер быстрого анализа
let salesSummary =
    sales
    |> List.groupBy (fun s -> s.Product)                          // Группируем по товару
    |> List.map (fun (product, items) ->                          // Преобразуем каждую группу
        let totalSales = items |> List.sumBy (fun s -> s.Amount)
        let avgSale = totalSales / decimal (List.length items)
        let topRegion =
            items
            |> List.groupBy (fun s -> s.Region)                   // Вложенная группировка
            |> List.maxBy (fun (_, regionItems) ->
                regionItems |> List.sumBy (fun s -> s.Amount))
            |> fst

        (product, totalSales, avgSale, topRegion))
    |> List.sortByDescending (fun (_, total, _, _) -> total)      // Сортируем по сумме продаж

// Отображаем результаты
salesSummary
|> List.iter (fun (product, total, avg, region) ->
    printfn "%s: $%M total, $%M avg, top region: %s"
        product total avg region)

Можно сохранить этот код в файле Sales.fsx,  и запустить его следующим образом:

dotnet fsi Sales.fsx

Теперь вы знаете, что F# — отличный выбор для написания скриптов! Кроме того, при запуске fsi dotnet откроется F# REPL, в котором можно изучать язык.

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

Совет:  взгляните на шпаргалку по F#, если вам нужен краткий справочник по синтаксису.

На меня хорошее впечатление произвело то, что разработчики языка стремились сделать F# понятным для новичков, добавив множество мелких удобных улучшений. Ниже приведено несколько примеров, которые для вас, вероятно, будут мало значить, но их оценят люди, знакомые с OCaml:

// строчные комментарии
(* классические комментарии ML тоже присутствуют *)

// изменяемые значения
let mutable x = 5
x <- 6

// диапазоны и срезы
let l = [1..2..10]
name[5..]

// Вызовы методов C# выглядят достаточно естественно
let name = "FOO".ToLower()

// операторы можно перегружать для разных типов
let string1 = "Hello, " + "world"
let num1 = 1 + 2
let num2 = 1.0 + 2.5

// универсальный вывод
printfn "%A" [1..2..100]

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

А я говорил о том, что в языке удобно работать со строками Unicode и регулярными выражениями?

Люди часто говорят, что F# — это по большей мере полигон для будущих фич C# и возможно, это правда. Я недостаточно долго наблюдал за обоими языками, чтобы иметь собственное мнение по этой теме, но меня впечатлило, что async/await (в C#, а потом и в JavaScript) впервые появились… в F# 2.0.

Всё изменилось в 2012 году, когда был выпущен C#5, где появилась популярная пара ключевых слов async/await. Эта возможность позволяла писать код со всеми преимуществами асинхронного кода, например, отсутствием блокировки UI при запуске долговременного процесса, однако код читался, как обычный синхронный код. Этот паттерн async/await сегодня добрался до множества современных языков программирования: Python, JS, Swift, Rust и даже C++.

Подход F# к асинхронному программированию немного отличается от async/await, но решает ту же задачу (на самом деле, async/await — это урезанная версия подхода F#, появившегося несколько лет назад в F#2).

— Айзек Эйбрахам, F# in Action

Время покажет, что будет дальше, но я не думаю, что C# когда-нибудь сможет полностью заменить F#.

Кроме того, я обнаружил вдохновляющий комментарий 2022 года о том, что Microsoft, возможно, готова вкладываться больше в F#:

Хорошие новости для вас. Спустя 10 лет разработки F# двумя с половиной людьми и с приходящей время от времени помощью от сообщества, Microsoft наконец-то решила вложиться в F# и создать этим летом полнофункциональную команду в Праге. Я разработчик из этой команды; как и вы, я давний фанат F# и очень рад, что дело наконец начало двигаться.

Если посмотреть на изменения между F# 8.0 и F 9.0, то можно прийти к выводу, что полнофункциональная команда отлично справляется!

Экосистема

После столь короткого периода изучения мне сложно оценить экосистему F#, но в целом мне кажется, что существует приличное количество «нативных» библиотек и фреймворков F#, а большинство людей активно использует API core .NET и множество сторонних библиотек и фреймворков, созданных для C#. Такое достаточно часто бывает с hosted-языками, так что в этом тоже нет ничего удивительного.

Если вы когда-нибудь пользовались другим hosted-языком (например, Scala, Clojure, Groovy), то, вероятно, знаете, чего можно ожидать.

Awesome F# ведёт список популярных библиотек, инструментов и фреймворков F#. Я перечислю здесь несколько библиотек для веб-разработки и data science:

Веб-разработка

  • Giraffe: легковесная библиотека для создания веб-приложений с использованием ASP.NET Core. Обеспечивает функциональный подход к веб-разработке.

  • Suave: простая и легковесная библиотека веб-сервера с комбинаторами для маршрутизации и построения задач. (Стала источником вдохновения для разработчиков Giraffe.)

  • Saturn: построена на основе Giraffe и ASP.NET Core; обеспечивает фреймворк в стиле MVC, вдохновлённый Ruby on Rails и Elixir Phoenix.

  • Bolero: фреймворк для создания клиентских приложений на F# с использованием WebAssembly и Blazor.

  • Fable: компилятор, транслирующий код на F# в JavaScript, что обеспечивает интеграцию с популярными экосистемами JavaScript наподобие React и Node.js.

  • Elmish: архитектура model-view-update (MVU) для создания веб-UI на F#; часто используется с Fable.

  • SAFE Stack: сквозной стек с упором на функциональное программирование для создания веб-приложений для облаков. Сочетает в себе такие технологии, как Saturn, Azure, Fable и Elmish, обеспечивая типобезопасный процесс разработки.

Data Science

  • Deedle: библиотека для манипуляций с данными и исследовательского анализа, схожая с pandas для Python.

  • DiffSharp: библиотека для автоматического дифференцирования и машинного обучения.

  • FsLab: коллекция библиотек, предназначенных для data science, в том числе инструменты визуализации и работы со статистикой.

Пока я не особо экспериментировал с ними, поэтому не буду пока давать никаких отзывов и рекомендаций.

Документация

Официальная документация достаточно хороша, однако мне кажется странным, что часть её хостится на сайте Microsoft, а часть — на https://fsharp.org/ (это сайт F# Software Foundation).

Мне очень понравились следующие части документации:

  • F# Style Guide

  • F# Design — репозиторий RFC (такой должен быть у каждого языка!)

  • F# Standard Library API

https://fsharpforfunandprofit.com/ — это ещё один хороший ресурс для обучения (хотя ощущается немного устаревшим).

Инструментарий разработки

История инструментария разработки F# довольно непростая — раньше поддержка F# была отличной только в Visual Studio, а в других местах довольно посредственной. К счастью, за последнее десятилетие ситуация с инструментарием улучшилась:

В 2014 году произошёл технический прорыв — Томас Петричек, Райан Райли, Дейв Томас и другие последующие контрибьюторы написали пакет FSharp.Compiler.Service (FCS). В нём содержится базовая реализация компилятора F#, инструментарий редактора и скриптовый движок в виде единой библиотеки; его можно использовать для создания инструментария F# в широком спектре ситуаций. Это позволило реализовать поддержку F# во множестве других редакторов, инструментов скриптинга и документирования, а также обеспечило разработку альтернативных бэкендов F#. Основным разрабатываемым сообществом редактором стал Ionide Кшиштофа Чешлака, используемый для поддержки удобного редактирования в кроссплатформенном редакторе VSCode, на момент написания статьи скачанный более одного миллиона раз.

— Дон Сайм, The Early History of F#

Я попробовал плагины F# для различных редакторов:

  • Emacs (fsharp-mode)

  • Zed (сторонний плагин)

  • Helix (встроенная поддержка F#)

  • VS Code (Ionide)

  • Rider (JetBrains .NET IDE)

В целом, Rider и VS Code обладают наибольшим количеством (и качеством) возможностей, но другими вариантами тоже вполне можно пользоваться. В основном это связано с тем, что LSP-сервер F# server fsautocomplete достаточно надёжен, и любой редактор с хорошей поддержкой LSP сразу получает богатую функциональность.

Тем не менее, должен сказать, в некоторых отношениях инструментарий хромает:

  • fsharp-mode не использует TreeSitter (пока) и, похоже, не особо активно разрабатывается (судя по коду, он производный от caml-mode)

  • Поддержка F# в Zed достаточно спартанская

  • В VS Code, как ни странно, поломано развёртывание и сворачивание выбранного, что довольно странно, ведь он должен быть основным редактором для F#

У меня возникают серьёзные проблемы с привязками клавиш в VS Code (слишком много клавиш-модификаторов и функциональных клавиш, на мой взгляд) и моделью редактирования, поэтому в дальнейшем я, вероятно, выберу Emacs. Или наконец-то качественно разберусь с neovim!

Похоже, что все пользуются одним и тем же форматировщиком кода (Fantomas), в том числе и команда F#, что здорово! С линтером история у F# не такая хорошая (похоже, единственный популярный линтер FSharpLint ныне заброшен), но когда компилятор настолько прекрасен, линтер особо не требуется.

Выглядит так, как будто Microsoft не особо вкладывается в инструментарий F#, ведь практически все крупные проекты в этой сфере разрабатываются сообществом.

ИИ-помощники в кодинге (например, Copilot) работают с F# достаточно хорошо, но я мало их тестировал.

В конечном итоге, вероятно, подойдёт любой редактор, если вы используете LSP.

Кстати, я сделал любопытное наблюдение в процессе программирования на F# (да и на OCaml) — когда работаешь на языке с очень хорошей системой типов, то от редактора на самом деле требуется не столь многое. Чаще всего мне вполне достаточно информации об inline-типах (например, что-то типа CodeLenses), автоматического дополнения и возможности простой отправки кода в fsi. Простота по-прежнему остаётся высшей степенью утончённости…

Другие инструменты, о которых следует знать:

  • Paket — менеджер зависимостей для проектов .NET. Можно считать его чем-то вроде bundler,  npm или pip, но для экосистемы пакетов NuGet .NET.

  • FAKE — DSL для создания задач и тому подобного, благодаря которому можно использовать F# для формулирования задач. Чем-то напоминает rake в Ruby. Некоторые считают его самым простым способом встраивания F# в уже существующий проект .NET.

Сценарии использования

Учитывая глубину и ширину .NET, вероятно, вас ничто не будет сдерживать!

Мне кажется, что F# особенно хорош для анализа данных и манипуляций с ними благодаря фичам наподобие поставщиков типов. Вот небольшая демонстрация работы поставщика типов JSON:

#r "nuget: FSharp.Data"

open System
open FSharp.Data

// Определяем тип на основании примера элемента JSON
type PeopleJson = JsonProvider<"""
[
  { "name": "Alice", "age": 30, "skills": ["F#", "C#", "Haskell"] }
]
""">

// Симулированный список JSON (может загружаться из файла или API)
let jsonListString = """
[
  { "name": "Alice",  "age": 30, "skills": ["F#", "C#", "Haskell"] },
  { "name": "Bob",    "age": 25, "skills": ["F#", "Rust"] },
  { "name": "Carol",  "age": 28, "skills": ["OCaml", "Elixir"] },
  { "name": "Dave",   "age": 35, "skills": ["Scala", "F#"] },
  { "name": "Eve",    "age": 32, "skills": ["Python", "F#", "ML"] },
  { "name": "Frank",  "age": 29, "skills": ["Clojure", "F#"] },
  { "name": "Grace",  "age": 27, "skills": ["TypeScript", "Elm"] },
  { "name": "Heidi",  "age": 33, "skills": ["Haskell", "PureScript"] },
  { "name": "Ivan",   "age": 31, "skills": ["Racket", "F#"] },
  { "name": "Judy",   "age": 26, "skills": ["ReasonML", "F#"] }
]
"""

// Парсим JSON
let people = PeopleJson.Parse(jsonListString)

// Выводим
printfn "People in the list:\n"
for p in people do
    printfn "%s (age %d) knows:" p.Name p.Age
    p.Skills |> Array.iter (printfn "  - %s")
    printfn ""

Когда я в первый раз увидел это, мне показалось это магией, ведь F# извлекает структуру и типы данных из небольшого примера данных, после чего мы получаем парсер для них. Можно сохранить код в файл TypeProvidersDemo.fsx, а потом запустить его следующим образом:

dotnet fsi TypeProvidersDemo.fsx

Более того, можно выполнять такие задачи, как извлечение данных напрямую из таблиц HTML и визуализация данных:

#r "nuget:FSharp.Data"
#r "nuget: Plotly.NET, 3.0.1"

open FSharp.Data
open Plotly.NET

type LondonBoroughs = HtmlProvider<"https://en.wikipedia.org/wiki/List_of_London_boroughs">
let boroughs = LondonBoroughs.GetSample().Tables.``List of boroughs and local authorities``

let population =
    boroughs.Rows
    |> Array.map (fun row ->
                  row.Borough,
                  row.``Population (2022 est)``)
    |> Array.sortBy snd
    |> Chart.Column
    |> Chart.show

Если запустить скрипт, то в вашем браузере появится красивая диаграмма населения разных районов Лондона. Отличная штука!

London Boroughs
London Boroughs

Здесь стоит также отметить простоту использования внешних библиотек (например, Plotly.NET) в скриптах на F#!

Думаю, F# хорошо подойдёт для бэкенд-сервисов и даже фулстек-приложений, хоть я особо и не изучал первые решения на F# в этой сфере.

Благодаря Fable и Elmish язык F# вполне можно использовать для программирования клиентской части; к тому же они могут позволить языку F# проникнуть в вашу повседневную работу.

Примечание:  в прошлом целевой платформой Fable был JavaScript, но с версии Fable 4 можно использовать и другие платформы, такие как TypeScript, Rust, Python и другие.

Вот пример того, насколько просто транспилировать кодовую базу F# на какой-нибудь другой язык:

# Транспиляция на JavaScript
dotnet fable

# Транспиляция на TypeScript
dotnet fable --lang typescript

# Транспиляция на Python
dotnet fable --lang python

Круто!

Сообщество

На первый взгляд, сообщество языка довольно маленькое, вероятно, даже меньше, чем у OCaml. Похоже, самые активные площадки для обсуждения F# — это Reddit и Discord (указанный в Reddit). Должен быть и ещё какой-то Slack по F#, но я не смог получить туда инвайта. (Кажется, автоматизированный процесс выпуска этих инвайтов уже какое-то время поломан.)

Я по-прежнему пока не очень понимаю, какую роль в сообществе играет Microsoft, потому что в целом сообщений от компании я видел не так много.

Для меня малый размер сообщества — не проблема, если оно остаётся активным и бурным. К тому же я заметил, что мне всегда были симпатичны маленькие объединения. При переходе от Java к Ruby перемена очень заметна, повышается вовлечённость в сообщество и чувство причастности.

Я не нашёл особо много книг и сайтов/блогов, посвящённых F#, но и не очень ждал этого.

Вот самые заметные общественные инициативы:

  • Amplifying F# — проект по продвижению F# и привлечению к нему компаний

  • F# for Fun and Profit — коллекция туториалов и эссе по F#

  • F# Lab — создаваемый сообществом инструментарий для data science на F#

  • F# Weekly — еженедельная рассылка о последних разработках в мире F#

Мне кажется, что для продвижения языка и привлечения новых программистов и бизнесов сделано более, чем достаточно, хоть это и всегда непросто для проекта с двадцатилетней историей. Я по-прежнему немного недоумеваю, почему Microsoft не продвигает F# активнее, ведь мне кажется, что он был бы отличным средством маркетинга.

Состязания в популярности

Люди по-разному относятся к «популярности» языков программирования. Меня часто спрашивают, почему я трачу много времени на языки, которые вряд ли принесут мне какие-то карьерные перспективы, например:

  • Emacs Lisp

  • Clojure

  • OCaml

  • F#

Возможности профессионального роста, разумеется, важны, но важно и:

  • развлекаться (а F в названии F# означает «fun»)

  • изучать новые парадигмы и идеи

  • бросать себе вызов, думая и работая иначе

Тем не менее, нужно отметить, что F# по большинству традиционных метрик остаётся непопулярным языком. У него не очень высокий рейтинг TIOBE, StackOverflow и на большинстве сайтов с вакансиями. Но в то же время он и не менее популярен, чем большинство «мейнстримных» функциональных языков программирования. Увы, функциональное программирование по-прежнему не мейнстримное, и, возможно, никогда таким не станет.

Ещё пара ресурсов по теме:

  • About F#«s popularity

  • How Popular is F# in 2024

    • Есть и видео к приведённой выше статье

F# и OCaml

Первоначальная концепция была простой F#: привнести преимущества OCaml в .NET и .NET в OCaml; создать союз между функциональным программированием со строгой типизацией и .NET. Здесь под «OCaml» подразумевается и само ядро языка, и прагматичный подход к функциональному программированию со строгой типизацией. Изначально задача была сформулирована чётко: я должен был реализовать заново ядро языка OCaml и часть его базовой библиотеки для целевой платформы .NET Common Language Runtime. Для юридической чистоты реализация должна была стать новой, то есть не использовать никакие части кодовой базы OCaml.

— Дон Сайм, создатель F#, The Early History of F#

Язык F# стал производным от OCaml, поэтому эти языки имеют много общей ДНК. На ранних этапах F# предпринимал усилия по поддержке максимально возможного объёма синтаксиса OCaml и даже позволял использовать расширения файлов .ml и .mli в коде на F#. Однако со временем пути языков начали расходиться3.

Разумеется, с самого начала рассматривалось создание языка, независимого от OCaml. Это отразилось и в выборе названия F#, хотя первые версии языка назывались Caml.NET:

Хотя первую версию F# презентовали, как «Caml для .NET», на самом деле, это всегда был новый язык, спроектированный с нуля для .NET. F# никогда не был полностью совместимым с какой-либо версией OCaml, хоть и имел общее совместимое подмножество и взял Caml-Light и OCaml за принципиальные источники вдохновения для своего дизайна.

— Дон Сайм, The Early History of F#

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

Плюсы F#

  • Работает на .NET

    • Доступна куча библиотек

  • Поддерживается Microsoft

  • Немного проще изучать новичкам (особенно тем, кто работал только с объектно-ориентированным программированием)

    • Немного проще разобраться в синтаксисе (мне кажется)

    • Ошибки и предупреждения компилятора более «дружественные» (их легче понять)

    • Проще отлаживать проблемы (частично связано с предыдущим пунктом)

  • Сильная поддержка асинхронного программирования

  • Имеет крутые фичи, которых нет в OCaml, например:

    • Анонимные записи

    • Активные паттерны

    • Вычислительные выражения

    • Sequence comprehension

    • Поставщики типов

    • Единицы измерения

Минусы F#

  • Работает на .NET

    • Взаимодействие с .NET повлияло на множество решений при проектировании языка (например, наличие допущения null)

  • Поддерживается Microsoft

    • Не всем нравится Microsoft

    • Похоже, Microsoft выделяет на F# довольно скромные ресурсы

    • Непонятно, насколько вовлечённой будет Microsoft в долговременной перспективе

  • Соглашения об именах: мне нравится snake_case гораздо больше, чем camelCase и PascalCase

  • Отсутствуют некоторые крутые фичи OCaml

    • Модули первого класса и функторы

    • GADT

  • Нет миленького логотипа с верблюдом

  • Название F# звучит круто, но поиск и имена файлов — это настоящий кошмар (довольно часто его называют FSharp)

И F#, и OCaml могут использовать в качестве целевой платформы среды выполнения JavaScript: F# при помощи Fable,  OCaml — при помощи Js_of_ocaml и Melange. На первый взгляд Fable кажется наиболее совершенным решением, но я недостаточно долго пользовался ими тремя, чтобы моё мнение было достаточно веским.

В конечном итоге, оба остаются практически одинаково надёжными, хотя и нишевыми языками, которые вряд ли станут очень популярными в будущем. Предполагаю, что вероятность профессиональной работы с F# выше, потому что .NET очень популярен; могу представить, что можно вставлять куски F# тут и там в готовые кодовые базы на C#.

Я заметил в проектах на F# одну странную штуку — в них по-прежнему используются XML-манифесты проектов (.fsproj), в которых нужно вручную перечислять файлы исходников в порядке их компиляции (чтобы учесть зависимости между ними). Меня поразило то, что компилятор не может обрабатывать зависимости автоматически, но, думаю, дело в том, что в F# нет прямого соответствия между файлами исходников и модулями. Как бы то ни было, мне гораздо больше нравится процесс компиляции OCaml (и Dune).

Так как мой интерес к ML в основном образовательный, я склоняюсь к OCaml, но если бы мне пришлось создавать веб-сервисы на ML-языке, то я бы, вероятно, выбрал F#. Ещё я крайне уважаю все языки с собственной средой выполнения, потому что маловероятно, что среда выполнения заставит идти на какие-то компромиссы в языке.

В заключение

Вопрос: что может C# и не может F#? Ответ: NullReferenceException!

— Шутка из сообщества F#

В конечном итоге, F# мне понравится гораздо больше, чем я ожидал! В каком-то смысле он напомнил мне прежний опыт работы с Clojure в том смысле, что Clojure был самым практичным Lisp из всех выпущенных, в основном благодаря своему отличному взаимодействию с Java.

У меня есть ощущение, что если бы .NET был портируемым (и опенсорсным) изначально, то ClojureCLR, вероятно, мог бы стать столь же популярным, как и Clojure, а F#, возможно, образовал более обширное сообщество и получил больше популярности. Я уверен, что больше никогда бы не связался с .NET, если бы не .NET Core, и сомневаюсь, что я один такой. Не способствовало популярности и то, что F# не был опенсорсным до 2010 года.

Кажется, так думаю не только я:

Ошибки сложно признавать, и они лучше понятны в историческом контексте. С самого начала огромной ошибкой для F# было то, что ни .NET, ни язык не были опенсорсными и не использовали открытой разработки. Эту ошибку хорошо понимали основные контрибьюторы, и многие люди в Microsoft призывали перейти на опенсорс. Если говорить просто, инновационный язык развивался в исследовательской лаборатории компании, которая ещё не была готова принять опенсорс: участники делали всё возможное благодаря периодической публикации исходников, а в конечном итоге проблема была решена переходом на опенсорсную разработку в 2011–2014 годах. Осознание этой ошибки — вероятно, самый важный шаг в истории языка. F# способен был развиваться в 2002–2011 годах, несмотря на закрытую разработку, в основном благодаря признанию его полезных качеств ответственными лицами в Microsoft.

— Дон Сайм, The Early History of F#

Изучать OCaml точно не очень сложно, но я думаю, что людям, желающим научиться какому-нибудь диалекту ML, будет проще с F#. И, как я говорил выше, его будет проще протолкнуть в «продакшен».

Мне кажется, любой, имеющий опыт в .NET, выиграет от изучения F#. А тот, кто хочет больше использовать возможности семейства ML-языков, определённо должен задуматься о F#, это отличный язык сам по себе, дающий вам доступ к одной из самых мощных платформ программирования.

И давайте не будем забывать о Fable, благодаря которому можно использовать F# в средах выполнения JavaScript, Dart, Rust и Python!

Итак, зачем же выбирать F#? В сообществе F# есть поговорка: «F» в слове F# обозначает «Fun». Судя по моему очень кратковременному опыту работы с F#, это правда! Более того, я считаю, что F# и по-настоящему fun, и по-настоящему практичный!

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

Вот и всё, что я хотел сказать.

Что дальше?

Если вам нужны другие аргументы для изучения F#, то крайне рекомендую следующие ресурсы:

  • F# Code I Love (доклад Дона Сайма)

  • Why Use F# Sharp?

  • Domain Modeling Made Functional

  • F# in Action

«The Early History of F#», которую я часто цитировал — тоже настоящее золото!

Также стоит подписаться на Reddit языка F# и присоединиться к Discord F#.

  1. У меня были курсы по C# в университете и я написал свой диплом бакалавра на C#. Это был переписанный pacman для Arch Linux, работающий в Mono. Произошло это в 2007 году.

  2. См. https://fsharp.org/history/hopl-final/hopl-fsharp.pdf

  3. https://github.com/fsharp/fslang-suggestions/issues/985

Habrahabr.ru прочитано 10933 раза