Pull to refresh

Об F# для архитекторов

Reading time 13 min
Views 8.6K
Original author: Chris Smith
Данная статья выросла из доклада, сделанного Крисом Смитом, одним из разработчиков F# на конференции DevLink. В целом она носит обзорный характер, однако несмотря на это, и на то, что усилиями shwars, mezastel и отчасти вашего покорного слуги F# уже не раз мелькал на Хабре, думаю что этот язык программирования не стал еще настолько очевиден всем, чтобы эта статья полностью потеряла смысл. Более того, здесь Крис пытается ответить на главный вопрос, который, думаю, мучает всех, кто хоть раз слышал о F#. Где же все-таки его применять? Получилось у него или нет – вам судить. Надеюсь после прочтения у вас хоть что-то относительно всего этого прояснится. Если так, я буду считать свою миссию перевыполненной :)
P.S. Первый перевод, поэтому не обессудьте за некоторые возможные баги.
image

После появления F# его конечно окружает много эмоций, но вопрос, который при этом занимает большинство людей – зачем это мне? Я думаю, мы можем предположить, что F# — это что-то хорошее, потому что иначе вряд ли бы Microsoft потратила столько времени и сил, чтобы поместить его в новую Visual Studio 2010. [Прим. перев. – слабенький аргумент :)]

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

Если вы программируете на C# или VB.NET, и при этом безмерно счастливы, то скорее всего у вас нет причин изучать F#. Просто оставайтесь счастливы. ;) Но если вы время от времени вынуждены бороться с собственным .NET кодом, тогда возможно F# — как раз то, что вы ждали.

Слухи


Об F# ходит много слухов. Вот некоторые из них.
  • Код, который вы сейчас пишете на C# завтра устареет.Неправда. F# не собирается заменять C# или VB. F# просто другой, поэтому какие-то вещи на нем делать проще, а какие-то – сложнее. Смысл в том, что имея F#, вы имеете выбор. И ниже мы попытаемся выявить некоторые из ситуаций, когда F# может быть предпочтительнее C#.
  • В F# можно бесплатно распараллеливать код.Неправда. Хотя F# (и другие ФЯП) позволяют проще распараллеливать код, он не делает это совсем задаром. Вообще говоря, вы можете написать те же самые блокировки или гонки на F#, что и на C#. Преимущество F# в том, что вам придется прежде явно нацелить ружье в ногу. (Прим. перев.: аллюзия в сторону старой программистской шутки о том, как в разных языках реализуется задача выстрелить себе в ногу.)
  • За функциональным программированием будущееНеправда. Функциональные языки программирования становятся в наше время объектами все более пристального внимания — Erlang, Scala, Nemerle, Haskell и.т.д. Тем не менее, ключевые идеи функционального программирования появились уже давно. И я не думаю, что функциональное программирование станет главной и единственной парадигмой, на которую все переключатся. Скорее, на мой взгляд, это хороший способ решить специфический класс задач. F# — гибридный язык, и он позволяет вам писать код в той парадигме, которая наилучшим образом подходит к текущей задаче.
  • Программисты F# умнее и лучше выглядят, чем другие.Правда. Абсолютная.
  • Вам стоит переписать все ваши ASP.NET приложения на F#.Неправда. В основном докладе на DevLink Джош Холмс особенно подчеркнул, что это плохая идея. Не то, чтобы сама связка F# + ASP.NET была плохой идеей, но желание использовать новые технологии только ради того, чтобы использовать новые технологии делает вещи сложнее, чем они должны быть.

Подведем черту под вышесказанным: вы должны понять, что такое F#, и если его использование сделает вашу работу более эффективной, значит используйте его. Если нет, используйте то, что вы уже знаете.


Язык


  • Как я уже отмечал выше, F# — мультипарадигменный язык программирования. Вы можете в нем писать функциональный, императивный и объектно-ориентированный код. Это позволяет вам быть более прагматичным, вместо того чтобы пытаться загнать любую задачу, стоящую перед вами, в прокрустово ложе классов и интерфейсов.
  • F# использует вывод типов, что приводит к более лаконичным программам. Подумайте о том, сколько времени вы проводите, добавляя сигнатуры типов, точки с запятыми, фигурные скобки в вашем любимом ЯП. Вся эта информация — лишь костыли, которые упрощают написание компилятора. Почему бы не писать тот код, который вы хотите, и предоставить компилятору вычислить остальное? Вы увидите позже, почему я считаю лаконичность F# его главной сильной стороной.
  • F# — это .NET-язык программирования. Он компилируется в IL в том же рабочем цикле что и C# с VB и будет запросто работать с уже существующим .NET-кодом.

Инструменты


  • F# включен в стандартный набор Visual Studio 2010, хотя присутствует и сейчас, в виде плагина для VS2008.
  • У него есть интерактивная среда программирования (REPL) как для Ruby и Python, называемая F# Interactive Window.

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

Важно отметить, что F# поддерживает почти все возможности, которые есть у C#. Поэтому вы можете использовать его не опасаясь принципа «все или ничего». Вам не нужно выбрасывать существующий код и переводить все на F#. Вообще, мы ожидаем, что код на F# будет главным образом использоваться как библиотеки классов, интегрированные в большой программный продукт. Возможно, некоторые компоненты сервера БД будут написаны на F#, в то время как остальной ваш код останется на C#/PHP/Smoke Signals/чем угодно.


Способность органично взаимодействовать с .NET открывает для F# множество возможностей. Вы можете не только использовать сильные стороны существующих инструментов, таких как дебаггер VS, IntelliSense, но и другие .NET платформы, например, использовать XNA, чтобы запустить F#-код на XBox, или Silverlight, чтобы запустить его в сети. Эта синергия является наверно главной причиной того, что F# уделяется намного больше внимания, чем более ранним языкам программирования как OCaml. OCaml великолепен, но поставляется ли он с дебаггером мирового уровня, визуализирующими библиотеками, и экосистемой, образованной сообществом разработчиков?

Но все-таки, зачем учить этот новый язык?

Начнем с того, что рассмотрим некоторые термины, связанные с F#.
  • Статически типизированный. F# является статически типизированным языком, в отличие, например, от динамически типизированного Ruby. Это означает, что информация о типах известна во время компиляции. Поэтому, хотя вы и не можете делать некоторые вещи, например обезяньи патчи, зато код исполняется намного быстрее и вы сможете узнавать о большинстве ошибок еще на этапе компиляции.
  • Лаконичный. В дополнение к выведению типов, F# также использует значимые пробелы. Если вы сместите строки под if-конструкцией на несколько пробелов вправо, компилятор расценит их как тело оператора. Это избавляет вас от необходимости засорять ваш код фигурными скобками и сохраняет для вас вертикальное пространство. Меньше кода на странице значит, что вы можете одновременно видеть большую часть программы, и меньше переключать контекст просматривая код. Может это звучит не слишком грандиозно, но на самом деле это будет большим подспорьем для вашей эффективности.
  • Расширяемый. Речь не о датацентрах и вебсерверах, а о том, что если вы применяете целостные принципы для разработки ПО к вашему коду на F#, вы сможете написать на нем приложения уровня предприятия. (Проще говоря, F# — не только язык для одноразовых утилиток.)
  • Исследуемый. В F# вам нет необходимости обращаться к дизайну, чтобы понять, как все работает. Вы можете быстро прототипировать решения F#, а также экспериментировать с алгоритмами и подходами с помощью F# Interactive Window. Опираясь на лаконичную природу F#, вы не будете писать много кода, чтобы экспериментировать с вашими программами.
  • Библиотеки. F# поставляется со своей собственной библиотекой для написания функционального кода и много чего еще.

Функциональное vs Объектно-Ориентированное


Функциональное программирование известно начиная с 50-х годов (LISP) — так почему оно так важно сейчас? Многие скажут, что такие вещи, как неизменяемость (immutability) помогают писать многопоточные программы, что на мой взгляд, не так уж важно. Потому что если вы хотите серьезно заниматься многопоточным программированием, есть ЯП созданные специально для того чтобы делать это и только это.
Главное преимущество функционального программирования лежит в том, что называется программированием в малом.
Что определяет текущее состояние дел в разработке программного обеспечения? — ООП. Главный путь, которым мы решаем задачу конвертации алгоритма в код — это разбиение его на объекты, эффективное моделирование предметной области с помощью классов и их поведения. Это — программирование в общем. Процесс абстракции работает отлично для моделирования больших систем и разбиения их на все меньшие и меньшие уровни, но, в конечном счете, вы упретесь в стену.
Что там с телами методов? Реальным кодом, который заключен в классах? Это — программирование в малом. Проблема с ООП в том, что когда вы пишете код алгоритмического типа, не имеет смысла создавать классы и интерфейсы. Когда вы программируете в малом, все что вы реально делаете — трансформируете данные и производите вычисления.
Представьте, что ООП — это бейсбольная ловушка. Она хорошо подходит, чтобы словить бейсбольный мяч, но далеко не так удобна, чтобы словить футбольный. Для этого вам требуется, чтобы пальцы на руках могли двигаться. Функциональное программирование и описывает суть программирования в малом. Оно сконцентрировано на манипулировании данными, которое требуется осуществить без заморочек с классами и заданной иерархией типов.

Области применения



Первая область, в которой F# в самом деле непревзойден это техническое и количественное программирование, т.е. в сложных математических расчетах.

Симуляция


Симуляция — одна из областей, которой F# хорошо подходит. Представьте, что вы пишете какого-то рода физический симулятор, или пытаетесь смоделировать некоторую реалистичную ситуацию. В F# вы можете аккуратно написать функции, которые вам требуются, без необходимости притягивать абстракции кода к процессам реального мира.
Другой аспект симуляций, который делает F# хорошим выбором для них — это единица измерения. Вы можете закодировать единицы измерения, такие как «футы» или «метры в секунду» в действующую систему типов F#. После этого вы получите предупреждение компилятора, если попробуете прибавить ускорение к скорости. Это позволяет вам предохраниться от целого ряда багов.

Вычислительные финансы


Некоторые из самых первых фирм, ставших использовать F#, занимались вычислениями финансов. Эти фирмы имеют большие базы данных для автоматической торговли на фондовых биржах, вычисления рисков и.т.д. (Как раз тот сорт компьютеризированных финансов, которые вызвали текущий глобальный экономический кризис :) ) F# предоставляет этим инвестиционным компаниям не только способ выразить их финансовые модели в более простом и декларативном виде, но также интегрироваться с остальным набором их приложений, через .NET взаимодействие. (Например, рассылка интенсивных вычислений на несколько машин через COM-сервис, и все в таком духе)

Обработка крупномасштабных данных


Наконец, F# хорошо справляется с задачами исследования данных. Например, представьте, что вы пытаетесь извлечь некоторую информацию о клиентах, чтобы распознать тенденции. В F# вы можете просто работать с вашими данными в F# Interactive Window, и нет необходимости засорять вашу машину десятками одноразовых проектов. Также, используя лаконичную модель программирования F# стоимость ошибки — реафакторинга или перепланирования вашего подхода заметно снижается.


Языково-ориентированное программирование


Следующий пункт — это ЯОП, которое означает размывание границы между ЯП и естественными языками. (Более подробно об использовании F# в ЯОП можно прочесть в данной статье Криса)

Написание парсеров


Если вы хотите быть продвинутым в доменно-специфичных языках, почему бы не пойти дальше, и не написать собственный парсер? А в библиотеке F# PowerPack имеются FSLex и FSYacc, два замечательных инструмента для создания парсеров. На самом деле, парсер компилятора F# создан с использованием FSLex и FSYacc.

Расширяемый F# код


Другой отличный пример ЯОП — это использование вычислительных выражений, которые имеются в F#. Это мощная возможность языка, которая позволяет вам писать нормальный F# код и кастомизировать способ его выполнения. Например, представим, что вы хотите написать некоторый F#-код для взаимодействия с роботом на Марсе. Вместо того, чтобы писать тонны шаблонного кода чтобы вновь попытаться отослать каждую команду, которую вы направляете вездеходу — например в случае космических препятствий или восстания марсиан, — вы можете написать вычислительное выражение (т.н. F# Workflow) чтобы автоматически перезапускать любой код F# несколько раз до определенного момента, или до тех пор, пока он не выполнится успешно.
(Это довольно сложная тема, которой на самом деле надо посвятить не одну статью)

Создание новых языков


Другая область, в которой F# превосходен — это создание внутренних доменно-специфичных языков (DSL, мини языков программирования, встроенных в ваш код). Размеченные объединения делают процесс создания абстрактных синтаксических деревьев (или любых других древоподобных структур) очень простым.

Expression Trees


Наконец, в F# есть Expression Trees, как и в C# и VB.NET. Это позволяет вам получить «компиляторно-сгенерированную» форму F# кода, которая вкупе с подходящими библиотеками может позволить вам отдать выполнение программы на другие платформы. Например, на сервер SQL или графический процессор вашей видеокарты.

Многопоточное программирование


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

Неизменяемость по умолчанию.


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

Parallel Extensions в .NET


Вообще говоря, эта функциональность относится не только к F#, но ко всему .NET. Длинный путь к тому чтобы сделать написание параллельных программ проще увенчался внедрением в четвертую версию .NET библиотеки Parallel Extensions (PFX). Хотя PFX предоставляет множество отличных возможностей платформе .NET, есть две вещи, которые мне особенно понравились.
Task Parallel Library (TPL) — это набор классов для абстрагирования процесса размещения «задач» программы. С помощью TPL можно забыть о необходимости вручную размещать задачи и управлять потоками. Вместо этого вы можете довериться TPL. Он сделает это за вас, он же отрегулирует число запущенных потоков, чтобы быть уверенным, что вы не перегрузите систему параллельными задачами.
Другая, и возможно даже более замечательная часть PFX это Concurrent Data Structures. Это новый набор типов коллекций, которые созданы специально для работы с высокопроизводительными многопоточными приложениями. Поэтому вместо того, чтобы контролировать все детали разделяемого изменяемого состояния, вы можете просто дать возможность писать обоим потокам в одну из этих конкурентных структур, и вся грязная работа с блокировками-разблокировками больше не будет вас заботить.

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

(подробно msdn.microsoft.com/en-us/library/ms978405.aspx)
Возможно вы слышали где-то раньше, что F# делает асинхронное программирование очень легким — это стопроцентная правда.

Для того чтобы писать асинхронные программы на .NET надо пользоваться Asynchronous Programming Model (APM). Это шаблон, в котором вы начинаете асинхронную операцию, например чтение из файла, вызывая BeginOp (т.е. BeginRead) и посылаете callback. Когда асинхронная операция завершается, выполняется callback, в котором вы должны вызвать EndOp (т.е. EndRead)
Хотя этот подход верой и правдой служил .NET на протяжении без малого десятилетия, с ним чертовски неприятно иметь дело.
  • Передача состояний через callback утомительна и провоцирует ошибки
  • Вам не так-то просто отлавливать и обрабатывать исключительные ситуации, если ваш код размазан среди мириадов callback'ов.
  • и. т. д.

На слайде вверху изображен код от группы разработчиков из Microsoft Patterns and Practices, и является рекомендованным способом обработки серии картинок в многопоточном режиме. Во всяком случае, нельзя не отметить, что здесь много кода, который выглядит не слишком просто.

А вот так можно написать ту же программу на F#, используя библиотеку F# Asynchronous Workflows. Как я уже много раз говорил, F# не был создан специально для того, чтобы писать многопоточные программы. F# Asynchronous Workflows — это всего лишь некоторая функциональность, встроенная в библиотеку F#, которая делает асинхронное программирование очень легким. Она включается через F# Computation Expressions.
В этом коде вы можете видеть 'let!' и 'do!'. F# Asynchronous Workflows выполняет это операции асинхронно, проворачивая всю деятельность со скаканьем по потокам и callback'ами за кадром.

Недостатки


Итак, я рассказал вам много о том, как замечателен F#, но давайте вернемся обратно к реальности. Вы же не верите, что F# — это сплошная сказка?

F# не подходит для всего, вот некоторые из таких областей.

Слой представления


В F# 1.0 мы не собираемся предоставлять никаких кодогенераторов. Поэтому вы не сможете воспользоваться никакими WYSIWYG редакторами из WinForms или WPF. Если вам хочется написать слой представления на F#, вам придется, к сожалению, делать все это руками. Тем не менее, это не конец света из-за .NET-вовлеченности F#. Вы можете просто создать ваш UI на C# и вызывать ваш F# код через библиотеки классов.

Объектно-ориентированные иерархии


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

Что же делать дальше, если вы решите, что F# подходит для некоторой части вашего приложения? Вам следует озаботиться наймом группы отвязных «физиков-ядерщиков»? Нет. Могу только повториться и от всего сердца прокричать громкое «НЕТ!». F# — не только для яйцеголовых! Например этим летом мы нанимали нескольких выпускников-интернов чтобы работать над некоторыми примерами F#.
В функциональном программировании по существу нет ничего сложного, просто оно рассматривает задачи с другой точки зрения. Главное – внимательно относиться к высказываниям ваших программистов, когда они изучают F#.
  • «Он почти такой же как C#». Это значит, что они не правильно его используют. Вся соль F# в том, что он предлагает другой образ мыслей о программировании, и в этом, другом подходе более эффективен. Если вы будете писать C#-код в F#, то ничего не выиграете.
  • «F# слишком прост для реальных задач». Несмотря на то, что F# прост для изучения, вы можете достичь довольно многого, даже если будете писать простые функции.
  • «Функциональное программирование выносит мой мозг». Это значит, что вы на верном пути. Изучение F# открывает вам другие подходы к решению проблем.

Поддержка


Пусть сегодня F# может помочь вам написать более продуктивный код, однако вопрос в том, как просто вы сможете управиться с ним завтра? Каковы издержки поддержки кода на F# в долгосрочном периоде?
Одно из главных преимуществ F# в том, что он короткий. Я уже отмечал это раньше, но скажу еще раз: когда вы дебажите код, возможность видеть весь относящийся к проблеме код на экране избавляет вас от необходимости постоянно переключать контекст между различными файлами кода.
Можно сказать, что галлюциногенный функциональный код сложнее для понимания, чем более обычный императивный. Это, конечно, правда, но правда и в том, что вам в первую очередь не стоит писать галлюциногенный код. (Точно так же как нет такой необходимости в любом другом языке программирования). Идиоматический код на F# ясен и читабелен. Вы естественно можете использовать и многие особенности языка, например композицию функций и каррирование, не слишком при этом усложняя код.

Вывод


В заключение повторюсь: если вам очень нравится использовать C# или VB, просто используйте их дальше. Вы можете получить пользу от изучения F#, равно как и от изучения любого другого языка, но не переключайтесь на F#, если для этого нет определенной причины.
Но если вам кажется, что ваш язык не позволяет в полной мере выразить вам свои идеи, засоряет ваш код ненужным синтаксическим мусором, в котором теряется смысл, попробуйте F#. В областях, связанных с большим количеством вычислений или преобразований данных – техническом программировании, ЯОП, параллельном/асинхронном – вы можете получить существенную пользу.
Написание кода на F# не сделает ваши программы, словно по мановению волшебной палочки, быстрее или менее ресурсоемкими. Все, что он делает – предоставляет вам возможность посмотреть на задачу с другой стороны, с которой ее решение может получиться более эффективным, и просто увеличивает количество путей для выражения ваших идей.
Итак, если вы еще не поставили себе F#, можете сделать это прямо сейчас: fsharp.net
Разработчики под Linux могут работать с F# в Mono. mono-project.com

Tags:
Hubs:
+38
Comments 33
Comments Comments 33

Articles