Этот текст является вольным переводом серии статей Sacha Barber из Brighton, UK , которые мне показались достаточно интересным

Это первый пост в моей серии по F#. Итак, что мы собираемся охватить? Как хорошо знают многие программисты, принято начинать с примера «Hello World».

Так что мы будем делать именно это. Итак, без лишних слов, что нужно для создания отдельного приложения «Hello World» на F#.

Вот оно:

open System

[<EntryPoint>]
let main argv = 
    printfn "Hello World" 
    Console.ReadLine() |> ignore
    0

//К сожалению, не нашёл выделение кода для F#

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

Так что именно здесь происходит?

Так как это отдельное приложение, нам, очевидно, нужна точка входа (так же, как и для любого другого языка .NET).

Итак, как определяется точка входа?

Ну, это почти то же самое, что и с другими языками .NET, у нас есть метод Main(или функция в F# терминологии), который принимает строковый массив, и мы используем атрибут [], чтобы указать, что метод с этим атрибутом является точкой входа.

Компилятор и Visual Studio любят нас за это.

Даже в этом примере для Вас должны звучать тревожные колокольчики или, по крайней мере, какое-то чувство непонимания. Итак, мы сказали, что есть начальная функция, которая принимает массив строкового типа. Все, что я вижу, это переменная с именем «argv», которую я не вижу объявленной, как массив. Эта переменная «argv» — это, очевидно, строковый массив, который мы ожидаем, но почему у него нет типа…. Ммм, интересно.

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

(variableName :variableType)

Поэтому, взяв приведенный выше пример «argv», мы могли бы вместо этого объявить вышеуказанную программу следующим образом, и это было бы хорошо:

open System

[<EntryPoint>]
let main (argv :string[]) = 
    printfn "Hello World" 
    Console.ReadLine() |> ignore
    0

Вы видите, что мы полностью определили имя входной переменной с ее типом, который в этом случае является массивом string [].

Что еще там интересует? Ну, как я говорил, есть о чем поговорить.

Почему у меня есть строка с этим странным |> ignore в конце. Что это?

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

  1. Открыть пространство имен «System», которое позволяет мне использовать типы пространства имен «System», почти так же, как если бы я использовал «using System» в C # или «Imports System» в VB .NET ».
  2. Так как F# является функциональным языком, он будет жаловаться, если функция, в данном случае это «Console.ReadLine (…)», которая может возвращать строку, не передаст результат далее функции «ignore», которая фактически означает return () (это способ в F# сказать void, но он называется Unit). Это явно указывает F# игнорировать результат вызова метода «Console.ReadLine (..)».

Например, вот как будет выглядеть код, если я решу не включать |> ignore

image

Программисту на C#/VB.NET может не увидеть, о чем здесь говорится. Но F# довольно жестко относится к тому, что функция может и не может делать. И одна вещь, которую функция ДОЛЖНА ВСЕГДА делать, это возвращать результат. В случае функции «main» ожидаемым результатом будет целочисленное значение 0.

Но как насчет случая «Console.ReadLine (..)»?

Хорошо, хоть в этом случае результат не имеет большого значения, и мы не заинтересованы в нем, и программа все равно будет работать нормально, если мы пропустим |> ignore, но если вы действительно не заинтересованы в результате, вы должны приучиться использовать |> ignore. Просто на тот случай, если вам интересно, конвейерный оператор «|>» направит возвращаемое значение левого выражения в правую функцию, что в данном случае равняется «принять возвращаемое значение Console.Readline (…) и игнорировать, как никому нет дела до него». Не беспокойтесь, если это ничего не значит для вас на данном этапе, мы еще поговорим об этом позже.

Последнее, что мне нужно сделать, это убедиться, что функция F# всегда возвращает значение, это делается в строке, где вы просто видите строку 0. Этого достаточно для того, чтобы F# понял, что это возвращаемое значение «0», которое система вывода типов выведет, это значение Int, равное 0. Причина, по которой компилятор F# знает, что это возвращаемое значение, заключается в том, что в данном случае это последнее утверждение, поэтому оно ДОЛЖНО быть возвращаемым значением. Очевидно, что это может усложниться с помощью условной логики if, else и т.д. Важно отметить, что в F# функция ДОЛЖНА ВСЕГДА возвращать значение, и в этом случае, поскольку значение 0 Int было последней найденной строкой (хотя все может быть более сложным), она используется в качестве возвращаемого значения. Вы, конечно, можете вернуть Unit, что делается с помощью «()», который фактически ничего не возвращает (void, если хотите).

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

Например, предположим, что мой тривиальный пример выглядел так в редакторе (в моем случае Visual Studio 2012):

image

Это крошечная программа, и ВСЕ, что я сделал, это удалил пробелы в начале строки «printfn». Мммм, похоже, что компилятору F# это совсем не понравилось. Да, это справедливо.

В F # пробел ОЧЕНЬ ОЧЕНЬ важен, так что играйте с ним хорошо, иначе привыкните к отладке загадочных ошибок, таких как показанная выше, которая вызвана простой проблемой некорректного семантического пробела.

Во всяком случае, это пример «Hello World». Пока мы не встретимся снова в следующий раз, я желаю вам всего хорошего.

Комментарии (7)


  1. danilstepa
    03.10.2019 20:23
    +3

    Люблю F#, но статья очень водяная к сожалению. Пользуясь случаем, хочу спросить, интересна ли будет сообществу статья о TypeProvider в F#? Уже несколько раз сталкивался с ситуациями когда их использование сильно упрощало жизнь, а на русском информации о них очень мало


    1. KvanTTT
      03.10.2019 21:02

      Лично мне — да, пишите, с удовольствием почитаем и плюсанем! :)


      1. tweekaz Автор
        03.10.2019 21:03

        Вторая часть через пару часов )


    1. Dimtry44
      03.10.2019 22:26

      интересна ли будет сообществу статья о TypeProvider в F#
      Хотел как-то сделать TypeProvider по информации из dacpac файлов, а то для MS SQL server есть, но для автогенерации кода не всегда подходит, не всегда есть соединение с сервером, довольно интересная тема.


      1. danilstepa
        03.10.2019 23:04

        С dacpac не знаком, возьму в заметки для ознакомления.

        Я думаю сделать 2 статьи.
        В первой описать несколько своих кейсов применения, в которых решение с провайдерами писалось на коленке и за смешной промежуток времени:
        1)Разбор CSV файла с более чем 50000 строк.
        2)Выборка данных из продуктовой базы, отправка данных в сторонний сервис, и разбор Json ответа в котором актуальны только несколько свойств.
        3)Парсинг данных с заданого сайта с получением только требуемых свойств.
        А во второй уже как они работают под капотом, и пример написания какого-нибудь легкого provider

        К сожалению, чукча, не писатель, потому кроме написания текста, придется еще подождать, когда его отредактируют:)


        1. Dimtry44
          03.10.2019 23:27

          В первой описать несколько своих кейсов применения
          Основной момент как это правильно отобразить в динамике. Самое интересное в typeprovider-ах это процесс разработки, а не результат.

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

          Может быть взять существующий и на его примере доработать для другого типа.

          Я кстати на волне впечатления от провайдров хотел запилить VS расширение «эмулятор TypeProvider» для C#. Типа берём провайдер написанный на F# вызываем его в плагине а потом делаем пропагандирование типов в C#, по идее используя Roslyn можно на лету генерировать/модифицировать код и создавать/удалять недостающие классы/свойства. Но так и не случилось.


          1. danilstepa
            04.10.2019 14:22

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