company_banner

F# на Linux как лекарство для души

    А у вас никогда не возникало ощущения, что «вот это» уже надоело? Что хочется чего-то нового? «Вот этим» может быть что угодно: игра, работа, машина. Что-то любое, что повторяется изо дня в день. А в программировании? Под катом вы найдете историю об усталости от C# и выборе более интересного подхода.



    Передаю слово автору.


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


    По большей части для меня важно, чтобы я вышел из зоны комфорта. В общем-то я так и сделал, сменил систему и язык.


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


    Основное:


    • Нужен легковесный редактор с поддержкой синтаксиса
    • Нужна поддержка F# Interactive
    • Нужна среда .NET
    • Нужен отладчик (впрочем, как и всегда)

    Искушенные хаброжители уже смекнули, что "легковесный редактор" + Microsoft = Visual Studio Code. Надеюсь, она у вас уже стоит :)


    Приступим к делу


    Итак, с выбором редактора разобрались, теперь со всем остальным по порядку:


    • Вам необходимо поставить пакет mono и fsharp, для большинства Linux систем инструкцию вы найдете здесь. Хоть в инструкции нет Arch Linux (и основанных на нем дистрибутивов), в стандартном репозитории pacman'a эти пакеты, впрочем, присутствуют, да и в инструкции показано как собрать из исходников, так что проблем возникнуть не должно.

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


    • Также поставим .NET Core 2.0.0 (Runtime + SDK), опять же, для большинства систем вы найдете инструкцию здесь. Для Arch Linux проще всего будет поставить пакеты dotnet и dotnet-sdk-2.0 из AUR. Если что, то вот github После этого, перейдем непосредственно уже к подготовке VS Code к нашей работе.

    Поставим расширения для поддержки синтаксиса F#, сборки и управлением пакетами Nuget.



    Для полноты можно поставить Nuget manager совместимый с .Net Core.



    Поставим расширение для отладки (Да, все правильно, написано C#).



    После этого клоним вот этот реп, и далее по инструкции делаем:


    dotnet restore && code .

    Теперь перезагружаем VS Code и ждем пока расширение "отладчика" докачает свои пакеты и нормально развернется.


    На самом деле, сейчас уже почти все готово, осталось только протестировать.


    • Создаем где-нибудь папку, запускаем там консоль. Далее выполняем:

    dotnet new console -lang F#
    dotnet run

    Получаем заветные Hello World from F#!


    • Открываем VS Code в этой папке code .
    • Идем в Program.fs и выделяем кусочек кода и жмем Alt+Enter:


    запустится долгожданный F# Interactive.



    Его также можно использовать для более полезных целей:



    Далее посмотрим сборку и отладку.


    • Для настройки директивы сборки нажимаем Ctrl+Shift+B и выбираем .Net Core.


    Автоматически сконфигурированный файл нам менять не нужно.


    • После этого снова нажимаем Ctrl+Shift+B и видим, что сборка происходит успешно:


    • После этого F5, выбираем также .Net Core и дальше просто меняем путь до программы.


    • Все, после этого можно поставить точку останова (F9) на нашей единственной строчке и проверить отладчик:


    Итак, ваша машина настроена и готова к новым свершениям на замечательном функциональном языке. Приятным бонусом будет то, что ваши наработки можно будет встроить в C# проект (посредством подключения .dll).


    Ну мало ли кто не знал)


    Об авторе



    Максимилиан Спиридонов — разработчик C#, студент МАИ, Microsoft Student Partner. В профессиональную разработку на .NET пришёл ещё в школе. Более 2,5 лет работает с реальными проектами на WPF(MVVM)+C#, MySQL, более 5,5 лет разрабатывал на C#. Основная сфера интересов сейчас — это мобильная разработка на Xamarin. Также, по воле случая в сфере интересов оказались С/С++ и Linux.


    Предыдущая статья Максимилиана о С/С++ на Linux в Visual Studio Code.

    Также приглашаем вас в чат по F# в Telegram.

    Microsoft

    231,00

    Microsoft — мировой лидер в области ПО и ИТ-услуг

    Поделиться публикацией
    Комментарии 39
      +3
      А что известного разрабатывают на F#? Какая область применения вообще?
        +3

        Jet.com например бекенд весь на F#. Объяснение почему


        От себя:
        F# умеет всё что и C# (ООП, наследование, интерфейсы, netcore 2.0 вот это вот всё) + много сверху (Discriminated Unions, partial application, type inference везде где можно, кастомные операторы и пр).
        Для создания бекенда прям самое оно. Система типов круче, ошибиться сложнее.
        Фронты писать в функциональном стиле тоже можно, попытки видел, мне лично не нравится.


        Есть просто отдельные крутые нугеты:
        Logary, Hopac

          +2
          type inference везде где можно

          А вот это немного неправда


          кастомные операторы

          С фиксированным приоритетом, зависящем от первого символа afaik

            +2
            А вот это немного неправда

            Почему же? Где можно, там выводит) Где не получается — надо ручками писать, да.


            С фиксированным приоритетом, зависящем от первого символа afaik

            Это намного лучше чем их полное отсутствие (дада, C#, я о тебе).
            Вполне хватает на любые нужды:
            >>=
            >?>
            >>=.
            |>
            ||>
            !>
            ?>
            и пр.
            Подробнее можно здесь посмотреть как сделать постфиксные, префиксные и инфиксные операторы — http://www.readcopyupdate.com/blog/2014/09/10/custom-ops-associativity-precedence.html

              0
              Это намного лучше чем их полное отсутствие

              Мой опыт Scala говорит, что полное отсутствие было бы лучше. Когда приоритет операции -> и + оказывается одинаковый, это доставляет не малое удовольствие.
              +1
              ну это уже неплохо.
              +3
              От себя и как бывший сотрудник Jet.com…

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

              Кроме плюшек, которые вы уже перечислили (discriminated unions, partial application, type inference, кастомные операторы и пр; система типов круче) есть еще очень важные immutability by default, bindings (async) и null safety (как очень важная разновидность крутой системы типов).

              Однако косяков в F# хватает тоже. Вобщем, кажется, что Microsoft подзабил на F# в последнее время. Кроме того, приходится писать базовые библиотеки, такие как клиентская библиотека для Apache Kafka или как моя FSharp.Json — в то время как какие-нибудь Java-девелоперы наслаждаются клиентом Kafka и Jackson и тратят время на решение бизнесс-задач, а не создание базовой инфраструктуры…
                +2

                Не, не подзабил, т.к. F# всегда был у Microsoft языком второго сорта) Их продукт — C#, а F# делается командой Don Syme на гитхабе.


                Поддержку .NET Core 2 в Visual Studio только-только завезли. Через год может быть паблиш F# проектов в Azure припилят =)


                По поводу инфраструктуры только частично соглашусь. Для большинства библиотек хватает вот этих функций


                let curry f a b = f (a, b)
                let curry3 f a b c = f (a, b, с)
                
                let uncurry f (a, b) = f a b
                let uncurry3 f (a, b, c) = f a b c

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


                Кстати Newtonsoft.Json умеет работать с F# из коробки (списки, массивы, DU) кроме option. Достаточно добавить один кастомный Option Converter чтобы вообще всё заработало.


                По поводу Jet.com любопытно. У них не так давно была волна агрессивного рекрутинга, где они предлагали "(high 5 to low 6 figures / year)", релокейт, но надо физически находиться в USA на момент обсуждения.
                Если не секрет почему ушли из Jet.com?

                  0
                  Нет, Newtonsoft не катит. Мы его пробовали очень долго и упорно. Вы говорите: «достаточно добавить option converter». Дело в том, что правильная трактовка option в JSON — может ли поле принимать значение null. Т.е. int option может быть в JSONе null и в таком случае десериализуется в None. А вот int не может быть null — должен быть Exception. Я уверен, что есть еще много других трактовок, но правильная (со всех сторон) именно эта. В Newtonsoft вы этого не добьетесь никак вообще. Кроме того, в Newtonsoft может выдать null и просто для int, вот так вот в рантайме при попытке использования (не сериализации) получить NPE — в F# это безусловное зло. Короче не поддерживает Newtonsoft null-safety. Как его надо поддерживать вот здесь.

                  И это я еще про поддержку юнионов даже не начинал…
                    0
                    Почему я больше не в Jet.com сказать пока не могу. Как-нибудь напишу статью, когда отпустит.
                +2

                Область применения схожа с С#, т.е. какие-то корп. решения. Вообще как инструмент очень полезен в сферах ML, кластерных вычислений, анализа больших данных, быстрого анализа данных. ± удобство отдельных библиотек, что-то более красиво и быстро можно написать на F#, нежели на С#, и встроить в более большой проект.


                Известного, ну вот например:
                https://www.microsoft.com/en-us/security-risk-detection/

                +1
                Отчего же не MonoDevelop?
                  +7

                  MonoDevelop не умеет отлаживать .NET Core по лицензионным соображениям. И не научится. По ним же. Такой вот открытый опенсорсный дотнет-стек от майкрософта.

                    +4

                    А почему Rider может? Там какие-то нюансы?

                      +6

                      Райдеровцы первоначально пытались использовать clrdbg (он же vsdbg), но наткнулись на лицензионные препятствия. Софтинка сия выдаёт на старте следующее:


                      You may only use the Microsoft .NET Core Debugger (vsdbg) with Visual Studio Code, Visual Studio or Visual Studio for Mac software to help you develop and test your applications.

                      В итоге отладки для .NET Core полгода у них не было, так как пришлось делать свою собственную обвязку вокруг libdbgshim.so, которая из Mono работает с COM-интерфейсами coreclr.


                      В MonoDevelop же такую обвязку завозить никто не собирается — это теперь просто базовая платформа для VSforMac и Unity, а не самостоятельная полноценная среда разработки.

                  +1

                  В два коротких сниппета доказываю что F# < OCaml. Задача: написать функции для доступа к первому и второму элементам пары.
                  OCaml: https://ideone.com/YirJoM
                  F#: https://ideone.com/9jPyMF

                    +5
                    Эм…
                    let fst (a, b) = a
                    let snd (a, b) = b
                      +1

                      Так неинтересно. В любом случае мой пример нерелевантен из-за разного смысла кода на OCaml и F#.

                        +1

                        Можно и так


                        let uncurry f (x, y) = f x y
                        let k x y = x
                        let k' x y = y
                        
                        let fst<'a,'b> = uncurry k
                        let snd<'a,'b> = uncurry k'
                        +1

                        Так pointless, извиняюсь, pointfree стиль — это самая мякотка функциональных языков.


                         ((+) .) . flip flip 2 . ((+) .) . (*)

                        Красота!

                          +4
                          Не то, чтобы я была фп-гуру, так что я могу и ошибаться. Но у меня есть подозрение, что читаемость и здравый смысл не обязательно класть на алтарь pointfree.
                      +2
                      Локализованная VSCode порадовала :)
                        +4
                        Что люди не сделают лишь бы на нормальном lisp не писать)
                          +2
                          На нормальном haskell. Зачем ограничиваться императивными языками? :)
                            +1

                            На нормальном Idris, зачем ограничивать себя System F/D? :^)

                              +1
                              Чтобы не писать сигнатуры для вообще всех функций, даже в where-блоках, конечно! Тем более, в ghc 8.8-8.10 завезут полноценные завтипы, при этом постаравшись сохранить вывод типов в большинстве случаев.

                              Ну и субъективно ленивость по умолчанию для языков с хаскелеподобным синтаксисом как-то более естественна.
                                0

                                Не для всех функций в where-блоках нужно писать сигнатуры, а для топлевел функций в хаскеле и так почти все их пишут. А ещё можно прямо из "дырки" создать функцию с сигнатурой.

                                  0
                                  В Idris-то? А можно пример?
                                    0
                                    fold : (a -> b -> b) -> b -> List a -> b
                                    fold op = flip go where
                                      go [] = id
                                      go (x :: xs) = go xs . op x

                                    Убедительно?

                                      0
                                      Вполне.

                                      Я тогда перестал понимать, когда сигнатуры не нужны.
                                        0

                                        In general, functions defined in a where clause need a type declaration just like any top level function. However, the type declaration for a function f can be omitted if:


                                        • f appears in the right hand side of the top level definition
                                        • The type of f can be completely determined from its first application
                                          0
                                          Интересно, я как-то упустил это. Спасибо!
                              0

                              Забыли добавить что большую часть времени будет тратится на возню и "срач" какие типы писать. Что затягивает сроки. Но не избавляет от ошибок.
                              При изменении бизнес требований, придется переписывать много когда, т.к. все жестко связано типами или вставлять костыли.

                                0
                                Я не против тратить на это большую часть времени, если интегрально это сэкономит времени на отладку. А по опыту — оно действительно экономит, и сроки не затягивает, причём что на одноразовой парсилке-агрегаторе логов, что на довольно крупномасштабных вещах вроде доморощенных компиляторов. И от ошибок избавляет, на самом деле. Не от всех, конечно, но когда тесты зелёные после любого рефакторинга, и краснеют они только тогда, когда ты играешься с самим алгоритмом — это круто.
                                  +1

                                  Не соглашусь с вашим тезисом, я очень долго работал на статически типизируемых языках со сложной системой типов: Java, Scala.
                                  По итогу я не нашел преимуществ. Код очень сильно разбухает из-за типов. Изменение в бизнес логике превращаются в переписывание большого куска кода или костыли.
                                  Разбираться в проектах написанных на языках со сложной системой типов очень трудно, если ты не автор. Нужно сперва изучить всю модель абстракции — за которой скрыта реальная логика работы.
                                  Если вы утверждаете о каких то домашних проектах то ок, вы в праве писать на чем угодно, но мне слабо верится что в ваш работодатель будет ждать пока вы отрефакторите свою "идеальную" систему типов под новую задачу.
                                  Скажу ещё так, используя Lisp с которым вы не согласились, а конкретно Clojure, вы напишете ваш проект значительно быстрее и ещё успеете все тестами прокрыть, за время которое вы обдумываете какие типы создать в Haskell, конечно если вам не хватит REPL'a для проверки всего (REPL oriented programming).

                                    +1
                                    Java

                                    Опыт системы типов джавы не очень релевантен хаскелю.

                                    Scala

                                    Это уже ближе, но это тоже не требует такой же строгости (в смысле чистоты функций и так далее).

                                    Код очень сильно разбухает из-за типов.

                                    По сравнению с чем?

                                    В случае Haskell — это вопрос топ-левел-аннотаций, да и те вы вполне можете опустить, они скорее полезны для общей читаемости и улучшения ругани тайпчекера.

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

                                    Но в языках со сложной статической системой типов на моей стороне есть хотя бы типы. Разбираться же в проектах, написанных на питоне/перле/схеме — это как разбираться в вермишели. Модифицировать их — вообще нет, лучше увольте.

                                    но мне слабо верится что в ваш работодатель будет ждать пока вы отрефакторите свою «идеальную» систему типов под новую задачу

                                    А не надо ждать. Наоборот, как-то так получается, что я на своих хаскелях задачи решаю быстрее, эффективнее и надёжнее, чем люди это делают на питонах. Про clojure или scheme не скажу, вокруг меня маловато людей, их знающих и их реально использующих.

                                    Очень яркий и недавний пример из моего опыта: компилятор для одного наркоманского язычка, когда не было ни формального ТЗ, ничего, только примеры кода на языке и хреновенькая неполная документация для пользователей, которая, по факту, была тоже сборником примеров. Рефакторить хоть фронтенд, хоть кодогенератор мне приходилось не раз и не два, просто потому, что я понимал о задаче что-то новое в процессе её решения, или перед сном мне приходила идея, каким трюком можно ускорить выполнение в N раз — и все эти рефакторинги были быстрые и безболезненные, и после непосредственной реализации алгоритма под вопросом всё просто работало.

                                    вы напишете ваш проект значительно быстрее и ещё успеете все тестами прокрыть, за время которое вы обдумываете какие типы создать в Haskell, конечно если вам не хватит REPL'a для проверки всего (REPL oriented programming)

                                    Какая-то ложная дихотомия. Типы у вас на самом деле и в самом последнем лиспе есть, просто лишь динамические. Это выгодно на начальном этапе развития программы, но через несколько дней и сот строк кода такой подход начинает серьёзно проигрывать.

                                    В хаскеле тоже есть REPL, кстати.
                                      0

                                      Вы считаете что написать много типов это так же быстро как их не писать. Для меня это звучит аналогично следующему — есть магазин в 100 метрах и я до него дойду за 1 минуту и есть магазин в 1000 метрах до которого вы добежите за 1 минуту, значит добираться до них как минимум однинаково по времени, а может и быстрее если бежать с большей скоростью.


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


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

                                        +1
                                        Для меня это звучит аналогично следующему — есть магазин в 100 метрах и я до него дойду за 1 минуту и есть магазин в 1000 метрах до которого вы добежите за 1 минуту, значит добираться до них как минимум однинаково по времени, а может и быстрее если бежать с большей скоростью.

                                        Только эта аналогия неверна.

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

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

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

                                        Я не трачу время на продумывание программы на типах, я просто продумываю программу. Типы данных и сигнатуры функций, где вместо реализаций понатыкано undefined — результат такого продумывания.

                                        Вы пишете код не продумывая, что ли?
                            +1
                            Друзья, когда Microsoft что-то всерьёз продвигает, то это происходит очень агрессивно (кроме просто рекламы в СМИ: слухи и их опровержения, скандалы в прессе, суды, обещания, разочарования, отставки и назначения, и море других страстей). Ничего похожего с F# и близко нет. Скорее это просто дань моде. Вот и всё.

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

                            Самое читаемое