Поделиться через


Рекомендации по форматированию кода (F#)

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

Основные правила использования отступов

Там, где требуется отступ, необходимо использовать пробелы, а не символы табуляции.Требуется как минимум один пробел.Организации могут создавать стандарты кодирования, в которых будет указываться количество пробелов, используемых для отступов; как правило, отступ на каждом уровне составляет три или четыре пробела.Настроить Visual Studio в соответствии с принятыми в организации стандартами отступов можно, изменив параметры в диалоговом окне Параметры, доступном из меню Сервис.Внутри узла Текстовый редактор разверните узел F# и щелкните элемент Табуляция. Описание доступных параметров см. в разделе "Параметры", "Текстовый редактор", "Все языки", "Вкладки".

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

Код в многострочной конструкции, такой как определение типа, определение функции, конструкция try...with и конструкции циклов, должен иметь отступ относительно первой строки конструкции.Первая строка с отступом устанавливает позицию колонки для последующего кода в этой же конструкции.Уровень отступа называется контекстом.Позиция колонки задает минимальную колонку, называемую офсайдной линией, для последующих строк кода в этом же контексте.При обнаружении строки кода, отступ которой не доходит до этой установленной позиции колонки, компилятор предполагает, что контекст закончился, и что последующий код находится на уровень выше, в предыдущем контексте.Под термином офсайд понимается ситуация, в которой строка кода провоцирует окончание конструкции, поскольку не имеет достаточного отступа.Иными словами, код слева от офсайдной линии находится "в офсайде".В коде с правильными отступами правилом офсайда удобно пользоваться для обозначения конца конструкций.При неправильном использовании отступов ситуация офсайда может стать причиной выдачи компилятором предупреждения или привести к неверной интерпретации кода.

Офсайдные линии определяются следующим образом.

  • Лексема =, связанная с ключевым словом let, устанавливает офсайдную линию в колонке первой лексемы после знака =.

  • В выражении if...then...else положение колонки первой лексемы после ключевого слова then или ключевого слова else устанавливает офсайдную линию.

  • В выражении try...with первая лексема после ключевого слова try устанавливает офсайдную линию.

  • В выражении match первая лексема после ключевого слова with и первая лексема после каждого оператора -> устанавливают офсайдные линии.

  • Первая лексема после ключевого слова with в расширении типа устанавливает офсайдную линию.

  • Первая лексема после открывающей фигурной или круглой скобки или после ключевого слова begin устанавливает офсайдную линию.

  • Первые символы в ключевых словах let, if и module устанавливают офсайдные линии.

Следующий пример кода иллюстрирует правила использования отступов.Операторы печати связываются с соответствующим контекстом по отступу.При каждом сдвиге отступа контекст извлекается из стека, и компилятор возвращается к предыдущему контексту.Следовательно, в конце каждой итерации печатается пробел; строка "Done!" печатается только один раз, поскольку отступ в офсайде указывает, что она не является частью цикла.Печать строки "Top-level context" не является частью функции.Следовательно, эта строка печатается первой — во время статической инициализации, до вызова функции.

let printList list1 =
    for elem in list1 do
        if elem > 0 then
            printf "%d" elem
        elif elem = 0 then
            printf "Zero"
        else
            printf "(Negative number)"
        printf " "
    printfn "Done!"
printfn "Top-level context."
printList [-1;0;1;2;3]

Выходные данные выглядят следующим образом.

Top-level context

(Negative number) Zero 1 2 3 Done!

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

let myFunction1 a b = a + b
let myFunction2(a, b) = a + b
let someFunction param1 param2 =
    let result = myFunction1 param1
                     param2
    result * 100
let someOtherFunction param1 param2 =
    let result = myFunction2(param1,
                     param2)
    result * 100

Из этих правил есть исключения, описанные в следующем разделе.

Отступы в модулях

Код в локальном модуле должен иметь отступ относительно модуля, однако код в модуле верхнего уровня не должен иметь отступа.Элементы пространства имен не должны иметь отступа.

Следующие примеры кода иллюстрируют это.

// Program1.fs
// A is a top-level module.
module A

let function1 a b = a - b * b
// Program2.fs
// A1 and A2 are local modules.
module A1 =
    let function1 a b = a*a + b*b

module A2 =
    let function2 a b = a*a - b*b

Дополнительные сведения см. в разделе Модули (F#).

Исключения из основных правил использования отступов

Общее правило, как описано в предыдущем разделе, состоит в том, что код в многострочных конструкциях должен иметь отступ относительно отступа первой строки конструкции, и что конец конструкции определяется местом прохождения первой офсайдной линии.Исключением из правила в части окончания контекстов является то, что некоторые конструкции, такие как выражение try...with, выражение if...then...else и использование синтаксиса and для объявления взаимно рекурсивных функций или типов, состоят из нескольких частей.Части выражения, следующие после первой, такие как then и else в выражении if...then...else, имеют тот же уровень отступа, что и лексема, с которой начинается выражение, однако вместо указания на конец контекста это означает следующую часть того же контекста.Следовательно, выражение if...then...else можно записать так, как показано в следующем примере кода.

let abs1 x =
    if (x >= 0)
    then
        x
    else
        -x

Исключение из правила офсайда относится только к ключевым словам then и else.Следовательно, хотя установка большего отступа перед ключевыми словами then и else не является ошибкой, отсутствие отступа перед строками в блоке then приводит к выдаче предупреждения.Следующие строки кода иллюстрируют это.

// The following code does not produce a warning.
let abs2 x =
    if (x >= 0)
        then
        x
        else
        -x
// The following code is not indented properly and produces a warning.
let abs3 x =
    if (x >= 0)
    then
    x
    else
    -x

К коду в блоке else применяется дополнительное специальное правило.Предупреждение в предыдущем примере возникает только в коде в блоке then, но не в коде в блоке else.Это позволяет писать код, проверяющий различные условия, в начале функции без установки отступа для остального кода функции, который может находиться в блоке else.Таким образом, можно написать следующий код и не получить предупреждения.

let abs4 x =
    if (x >= 0) then x else
    -x

Другим исключением из правила, что контексты заканчиваются, когда строка имеет отступ меньший, чем предыдущая строка, являются инфиксные операторы, такие как + и |>.В строках, начинающихся с инфиксных операторов, колонки (1 + oplength) могут начинаться до нормальной позиции, не провоцируя окончания контекста; oplength здесь — количество символов, из которых состоит оператор.В результате этого первая лексема после оператора выравнивается по предыдущей линии.

Например, в следующем коде символ + может иметь доступ на две колонки меньший, чем предыдущая линия.

let function1 arg1 arg2 arg3 arg4 =
    arg1 + arg2
  + arg3 + arg4

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

Сброс позиции колонки допускается в следующих конструкциях.

  • Тела анонимных функций.В следующем коде выражение печати начинается в позиции колонки, находящейся левее ключевого слова fun.Однако строка не должна начинаться в колонке, расположенной слева от начала предыдущего уровня отступа (т. е. слева от буквы L в слове List).

    let printListWithOffset a list1 =
        List.iter (fun elem ->
            printfn "%d" (a + elem)) list1
    
  • Конструкции, заключенные в круглые скобки или между ключевыми словами begin и end в блоке then или else выражения if...then...else, при условии, что отступ не меньше позиции колонки ключевого слова if.Это исключение делает возможным стиль написания кода, когда открывающая круглая скобка или ключевое слово begin ставятся в конце строки после ключевого слова then или else.

  • Тела модулей, классов, интерфейсов и структур, разделенные ключевыми словами begin...end, {...}, class...end или interface...end.Это делает возможным стиль, когда открывающее ключевое слово определения типа может находиться на той же строке, что и имя типа, без необходимости сдвигать все тело относительно открывающего ключевого слова.

    type IMyInterface = interface
       abstract Function1: int -> int
    end
    

См. также

Другие ресурсы

Справочник по языку F#