Сообственно у Nemerle пока нет документа, который бы описывал как принято писать код.
Может стоит начать обсуждение здесь и таким образом сформировать документ ?
Отталкиваться можно от стиля RSDN на C#, например, и добавлять необходимые изменения по мере обсуждения.
Здравствуйте, _nn_, Вы писали:
__>Сообственно у Nemerle пока нет документа, который бы описывал как принято писать код. __>Может стоит начать обсуждение здесь и таким образом сформировать документ ?
Кое что есть, но оно на сайте, а но лежит.
__>Отталкиваться можно от стиля RSDN на C#, например, и добавлять необходимые изменения по мере обсуждения.
Во многом стиль кодирования шарпа не применим для немерле. Скажывается функциональная природа и особенности языка.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Don Reba, Вы писали:
DR>Я следую стандарту RSDN. Необходимых изменений даже, как-то сразу на ум не приходит.
На самом дела слепое следование стандартам для шарпа приводит к некоторым проблемам.
1. Функциональный код часто лучше оставить на одной строке нежели резать на строки (без переменных). Особенно это отчетливо видно на операторе match.
2. Многие конструкции вроде if/else хотя и похожи на шарповские, но отличаются от них.
3. Отсутствие return так же делает код другим.
4. Есть конструкции не имеющие аналогов в шарпе. Для них вообще нет правил форматирования (опять же match).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Don Reba, Вы писали:
DR>Я следую стандарту RSDN. Необходимых изменений даже, как-то сразу на ум не приходит.
В исходниках самого компилятора NCC нет четкого стиля форматирования кода.
В одном и том же файле используются разные стили расставления скобок:
Ну и ещё, не удобно то, что отступы в начале строк делаются пробелами. Для этого всё-таки есть табы. Но поскольку для некоторых это холиварная тема, наверное стоит сделать автоматическое форматирование кода кнопкой в редакторе.
Не понятно насчет точек с запятыми, они расставлены абсолютно рандомно: после match'а нужна точка с запятой?
Здравствуйте, Aleх, Вы писали:
A>после match'а нужна точка с запятой?
match — это оператор. Он может быть как отдельным выражением и тогда после него нужна точка с запятой, если есть идущие следом выражения. Так же он может встречаться в выражениях как подвыражение. Тогда после него просто нельзя ставить точку с запятой.
Что до стиля внутри компилятора, то можно скзать, что в нем действительно смешение стилей. Я к этому приложил свою руку.
На мой взгляд некоторые аспекты стиля форматирования выбранные первыми авторами языка неудобны.
Посему лично я объединил принципы форматирования выработанные в рамках РСДН (описаны на нашем сайте) и принципы специфичные для Немерле.
Могу чуть позже описать стандарт форматирования которого я придерживаюсь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>На самом дела слепое следование стандартам для шарпа приводит к некоторым проблемам.
Ну, это само собой.
VD>1. Функциональный код часто лучше оставить на одной строке нежели резать на строки (без переменных). Особенно это отчетливо видно на операторе match. VD>2. Многие конструкции вроде if/else хотя и похожи на шарповские, но отличаются от них. VD>3. Отсутствие return так же делает код другим. VD>4. Есть конструкции не имеющие аналогов в шарпе. Для них вообще нет правил форматирования (опять же match).
Для match один к одному подходят рекомендации для switch/case. Разве нет?
match с длинными последовательностями в case:
match (condition)
{
| 1
| 2 =>
x = ...;
| 3 =>
x = ...;
| _ =>
x = ...;
}
match с короткими последовательностями в case:
match (condition)
{
| 1 => x = 1;
| 2 => x = 2;
| 3 => x = 3;
| _ => x = 100;
}
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, Don Reba, Вы писали:
DR>>Я следую стандарту RSDN. Необходимых изменений даже, как-то сразу на ум не приходит.
__>Ну к примеру как лучше писать match?
Придерживаюсь первого варианта, аналогично использованию switch:
__>1. __>
__>match(a)
__>{
__> | h :: t => ...
__> | h :: [] => ...
__> | _ => ...
__>}
__>
__>Использование match в функции. __>Использовать явно или неявно ?
А это действительно не определить по стандарту RSDN. Я предпочитаю неявную запись.
__>1. __>
__>def f(a)
__>{
__> | h :: t => ...
__> | h :: [] => ...
__> | _ => ...
__>}
__>
Здравствуйте, Don Reba, Вы писали:
DR>Для match один к одному подходят рекомендации для switch/case. Разве нет?
Нет. Во-первых, как правильно заметил _nn_ есть многие норовят не отбивать | а оставлять его на уровне открывающей фигурной скобкой. Во-вторых, первый | можно опускать. В-третьих, есть случаи (и они часты) когда выражения влезают в ту же строку где находится =>. В-четвертных, в match нет break и если не отбивать выражения перенесенные на следующую строку пустой строкой, то оно сливается со следующим паттерном.
В общем, есть много нюансов.
DR>match с короткими последовательностями в case:
DR>
DR>match (condition)
DR>{
DR> | 1 => x = 1;
DR> | 2 => x = 2;
DR> | 3 => x = 3;
DR> | _ => x = 100;
DR>}
DR>
Дык, а этого нет в (если я не ошибаюсь) в правилах для шарпа.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Нет. Во-первых, как правильно заметил _nn_ есть многие норовят не отбивать | а оставлять его на уровне открывающей фигурной скобкой.
Многие норовят и case не отбивать. В этом match не отличается.
VD>Во-вторых, первый | можно опускать.
Вот это уникально. ИМХО, имеет смысл только когда весь match записывается в одну строчку.
VD>В-третьих, есть случаи (и они часты) когда выражения влезают в ту же строку где находится =>.
Для этого есть рекомендация для "коротких последовательностей". Тут match тоже не отличается от switch.
VD>В-четвертных, в match нет break и если не отбивать выражения перенесенные на следующую строку пустой строкой, то оно сливается со следующим паттерном.
У выражения же отступ больше чем у образца. Как они могут слиться?
Здравствуйте, Don Reba, Вы писали:
DR>Многие норовят и case не отбивать. В этом match не отличается.
ОК.
DR>Вот это уникально. ИМХО, имеет смысл только когда весь match записывается в одну строчку.
По-моему, это вообще надо бы выбросить чтобы лишних вопросов не было.
DR>Для этого есть рекомендация для "коротких последовательностей". Тут match тоже не отличается от switch.
По-моему в switch не разрешалось никаких коротких последовательностей. Хотя лично я от этого сам много раз отходил.
VD>>В-четвертных, в match нет break и если не отбивать выражения перенесенные на следующую строку пустой строкой, то оно сливается со следующим паттерном.
DR>У выражения же отступ больше чем у образца. Как они могут слиться?
Это надо видеть на практике. Смотришь на код и не пожмешь понять где заканчивается выражение, а где начинается следующий паттерн. В общем, я всегда отбиваю.
Это вообще все конечно вкусощина. Но когда приходится анализировать много ЧУЖОГО кода, то эта вкусовщина начинает становиться очень болезненной.
Чтобы разобраться в коде компилятора я не раз был вынужден переформатировать код.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, _nn_, Вы писали:
__>Сообственно у Nemerle пока нет документа, который бы описывал как принято писать код. __>Может стоит начать обсуждение здесь и таким образом сформировать документ ?
__>Отталкиваться можно от стиля RSDN на C#, например, и добавлять необходимые изменения по мере обсуждения.
В Nemerle есть #pragma indent. Если она написана правильно — это и должен быть Nemerle coding style...
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, _nn_, Вы писали:
__>>Сообственно у Nemerle пока нет документа, который бы описывал как принято писать код. __>>Может стоит начать обсуждение здесь и таким образом сформировать документ ?
__>>Отталкиваться можно от стиля RSDN на C#, например, и добавлять необходимые изменения по мере обсуждения.
А>В Nemerle есть #pragma indent. Если она написана правильно — это и должен быть Nemerle coding style...
Что значит написана правильно ?
Т.е. вы только за #pragma indent как основого стиля ?
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Don Reba, Вы писали:
DR>>Многие норовят и case не отбивать. В этом match не отличается.
VD>ОК.
DR>>Вот это уникально. ИМХО, имеет смысл только когда весь match записывается в одну строчку.
VD>По-моему, это вообще надо бы выбросить чтобы лишних вопросов не было.
+1
Может стоит сейчас запретить пропускать первый | в match ?
Здравствуйте, _nn_, Вы писали:
А>>В Nemerle есть #pragma indent. Если она написана правильно — это и должен быть Nemerle coding style...
__>Что значит написана правильно ? __>Т.е. вы только за #pragma indent как основого стиля ?
Она задумана как в питоне — неправильно отформатировал — не скомпилится, так что стиль отступов предопределен.
Описывать подробно весь стиль кодирования в лом, так что я приведу пример кода и дам коментарии.
using System;
using System.Console;
using Nemerle.Utility;
[Record]
variant Token // имена типов ПаскальКейс, как в РСДН
{
| Number { value : int; } // если описание полей варианта влезает в одну строку
| Operator
{
name : string; // если не влезает, то каждое поле (и другие члены) на отдельной строке
}
| OpenBrace // пустых строк между вхождениями нет.
| CloseBrace
| Eof
| Error
public StartPos : int;
#region object Members
public override ToString() : string
{
match (this) // пробел между именем оператора и скобками (как в РСДН)
{ // каждое вхождение отбито на одну табуляцию
| Number(value) => $"$value"// по возможности используется табличное форматирвоание
| Operator(name) => $"$name"
| OpenBrace => "("
| CloseBrace => ")"
| Eof => "."
| Error => "Error"
}
}
#endregion object Members
}
module Program
{
Main() : void// используются алиасы типов, а не полные имена вроде System.Void
{
def calc(text : string) // имена локальных функций и переменных в кэмелКэйсе
{
def lexer(text : string) : list[Token] // типы у локальных функций указываю только когда это помогает понят суть функции
{
mutable index = 0;
def peek()
{
if (index >= text.Length) '\0'else text[index];
} // пустых строк между описаниями локальных функций нет
def read()
{
def ch = peek(); // переменные желательно отбивать от остального кода одной строкой (не строгое правило)
when (index < text.Length)
index++; // выражение внутри макры "when" всегда на новой строке и всегда заканчивается ";"
ch
}
def isDigit(ch) { ch >= '0' && ch <= '9' } // мелкие функции можно описывать инлайном
def number(ch : char, accumulator : int = 0) : int
{ // опять же табличное форматирование, где одно улучшает читаемость.
def highOrderValue = accumulator * 10; // если переменные повышают читабельность, то разносим
def currentOrderValue = ch - '0' : int; // выражение на несколько строк и для его частей вводим перепменные
def currentValue = highOrderValue + currentOrderValue;
if (isDigit(peek())) // если ветка if-а - это одно выражение, то не в коем случае не используем { и } !
number(read(), currentValue) // если значение выражения является возвращаемым значением функции, то ";" не ставим!else
currentValue
}
def loop(res : list[Token]) : list[Token]
{ // все открывающие скобки, кроем тел лябд и именованных блоков, начинаются с новой стоки
def ch = read();
match (ch)
{
| '(' => loop(Token.OpenBrace(index) :: res)
| ')' => loop(Token.CloseBrace(index) :: res)
| ' ' | '\t' => loop(res) // игнорируем пробелы
| '+' | '-' | '*' | '/' => loop(Token.Operator(index, ch.ToString()) :: res)
| '\0' => res.Reverse()
| _ when isDigit(ch) => loop(Token.Number(index, number(ch)) :: res)
| _ => // если вхождение match-а не влазит в одну строку, то...
// переносим все выражения на следующую строку и начинаем их с отбивкой в одну табуляцию от "|" (начала паттерна)
WriteLine(string(' ', index - 1) + "^");
WriteLine($"ожидается число или оператр (в позиции $index)");
(Token.Error(index) :: res).Reverse() // опять же возвращаемое значение не заканчивается ";"
}
}
loop([])
}
def parseAndEval(tokens : list[Token])
{
def eof = Token.Eof(if (tokens.IsEmpty) 1 else tokens.Last.StartPos + 1);
mutable current = tokens;
def peek()
{
match (current)
{
| null => Token.Error(1)
| head :: _ => head
| [] => eof
}
}
def read()
{
match (current)
{
| null => Token.Error(1)
| head :: tail =>
current = tail;
head // если во вхожднеии более одной строки и ниже есть еще вхождения, то отделяем ее пустой строкой
| [] => eof
}
}
def error(token : Token, message : string) : int
{
WriteLine(string(' ', token.StartPos - 1) + "^");
WriteLine(message);
current = null;
0
}
// SimpleExpr = Number | '-' SimpleExpr | '(' SumOrSub ')'
// MulOrDive = SimpleExpr (('*' | '/') SimpleExpr)*
// SumOrSub = MulOrDive (('+' | '-') MulOrDive)*
def parseSimpleExpr() : int
{
def tok = read();
match (tok)
{
| Error => 0
| Operator("-") => - parseSimpleExpr()
| Number(val) => val
| OpenBrace =>
def value = parseSumOrSub();
def toc2 = read();
if (toc2 is Token.CloseBrace || toc2 is Token.Error)
value
else
error(tok, "Не закрытая скобка!")
| _ => error(tok, "Ожидается число, унарный '-' или '('")
}
}
and parseMulOrDive() : int
{
def loop(firstValue)
{
def tok = peek();
match (tok)
{
| Operator("*") =>
_ = read();
def secondValue = parseSimpleExpr();
loop(firstValue * secondValue)
| Operator("/") =>
_ = read();
def secondValue = parseSimpleExpr();
if (secondValue == 0)
error(tok, "Делитель не должен быть нулем!")
else
loop(firstValue / secondValue)
| _ => firstValue
}
}
loop(parseSimpleExpr())
}
and parseSumOrSub() : int
{
def loop(firstValue)
{
def tok = peek();
match (tok)
{
| Operator("+") =>
_ = read();
def secondValue = parseMulOrDive();
loop(firstValue + secondValue)
| Operator("-") =>
_ = read();
def secondValue = parseMulOrDive();
loop(firstValue - secondValue)
| Number | OpenBrace => error(tok, "Ожидается оператор или конец выражения")
| _ => firstValue
}
}
def firstValue = parseMulOrDive();
loop(firstValue)
}
def parse() : int
{
def value = parseSumOrSub();
def tok = peek();
when (!(tok is Token.Eof) && !(tok is Token.Error))
_ = error(peek(), $"Не ожидаемый токен '$(peek())'");
value
}
def res = parse();
when (current != null)
{
WriteLine("Результат:");
WriteLine(res);
}
}
def res = lexer(text);
unless (res.IsEmpty || res.Last is Token.Error)
parseAndEval(res);
}
def readInput()
{
def res = ReadLine();
unless (res == "")
{
calc(res);
readInput();
}
}
readInput();
}
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
VD>Описывать подробно весь стиль кодирования в лом, так что я приведу пример кода и дам коментарии.
VD>
VD>using System;
VD>using System.Console;
VD>using Nemerle.Utility;
VD>[Record]
VD>variant Token // имена типов ПаскальКейс, как в РСДН
VD>{
VD> | Number { value : int; } // если описание полей варианта влезает в одну строку
VD> | Operator
VD> {
VD> name : string; // если не влезает, то каждое поле (и другие члены) на отдельной строке
VD> }
VD> | OpenBrace // пустых строк между вхождениями нет.
VD> | CloseBrace
VD> | Eof
VD> | Error
VD> public StartPos : int;
VD> #region object Members
VD> public override ToString() : string
VD> {
VD> match (this) // пробел между именем оператора и скобками (как в РСДН)
VD> { // каждое вхождение отбито на одну табуляцию
VD> | Number(value) => $"$value"// по возможности используется табличное форматирвоание
VD> | Operator(name) => $"$name"
VD> | OpenBrace => "("
VD> | CloseBrace => ")"
VD> | Eof => "."
VD> | Error => "Error"
VD> }
VD> }
+1
VD> #endregion object Members
VD>}
VD>module Program
VD>{
VD> Main() : void// используются алиасы типов, а не полные имена вроде System.Void
VD> {
VD> def calc(text : string) // имена локальных функций и переменных в кэмелКэйсе
VD> {
VD> def lexer(text : string) : list[Token] // типы у локальных функций указываю только когда это помогает понят суть функции
VD> {
VD> mutable index = 0;
VD> def peek()
VD> {
VD> if (index >= text.Length) '\0'else text[index];
Почему не на отдельных строках ?
Мне кажется однострочное if выражение лучше использовать только при инициализации.
def x = if(y > 1) f() else g();
VD> } // пустых строк между описаниями локальных функций нет
И далее идут пустые строки между локальными функциями.
На мой взгляд не стоит делать это обязательным.
VD> def read()
VD> {
VD> def ch = peek(); // переменные желательно отбивать от остального кода одной строкой (не строгое правило)
Как раз всегда получается что создается одна пустая строка, чтобы повысить читаемость.
Думаю стоит это сделать строгим правилом.
VD> when (index < text.Length)
VD> index++; // выражение внутри макры "when" всегда на новой строке и всегда заканчивается ";"
+1
VD> ch
VD> }
VD> def isDigit(ch) { ch >= '0' && ch <= '9' } // мелкие функции можно описывать инлайном
VD> def number(ch : char, accumulator : int = 0) : int
VD> { // опять же табличное форматирование, где одно улучшает читаемость.
+1
Кстати, а в интеграции есть опция для создания табличного форматирования ?
VD> def highOrderValue = accumulator * 10; // если переменные повышают читабельность, то разносим
VD> def currentOrderValue = ch - '0' : int; // выражение на несколько строк и для его частей вводим перепменные
VD> def currentValue = highOrderValue + currentOrderValue;
VD> if (isDigit(peek())) // если ветка if-а - это одно выражение, то не в коем случае не используем { и } !
+1
Только за. Мне не нравятся лишние фигурные скобки когда они не нужны.
VD> number(read(), currentValue) // если значение выражения является возвращаемым значением функции, то ";" не ставим!
Я так понимаю это для того чтобы было понятно что есть возвращаемое значение ?
VD> else
VD> currentValue
VD> }
VD> def loop(res : list[Token]) : list[Token]
VD> { // все открывающие скобки, кроем тел лябд и именованных блоков, начинаются с новой стоки
VD> def ch = read();
VD> match (ch)
VD> {
VD> | '(' => loop(Token.OpenBrace(index) :: res)
VD> | ')' => loop(Token.CloseBrace(index) :: res)
VD> | ' ' | '\t' => loop(res) // игнорируем пробелы
VD> | '+' | '-' | '*' | '/' => loop(Token.Operator(index, ch.ToString()) :: res)
VD> | '\0' => res.Reverse()
VD> | _ when isDigit(ch) => loop(Token.Number(index, number(ch)) :: res)
VD> | _ => // если вхождение match-а не влазит в одну строку, то...
VD> // переносим все выражения на следующую строку и начинаем их с отбивкой в одну табуляцию от "|" (начала паттерна)
VD> WriteLine(string(' ', index - 1) + "^");
VD> WriteLine($"ожидается число или оператр (в позиции $index)");
VD> (Token.Error(index) :: res).Reverse() // опять же возвращаемое значение не заканчивается ";"
VD> }
Тот же вопрос насчет автоматического табличного форматирования ?
VD> }
VD> loop([])
VD> }
... <skip/>
Постараюсь добавить в документ стиля RSDN поправки и будет документ для Nemerle