Я как-то возмущался, что в Немероле используются поля тогда, когда кошерно использовать свойства.
Сейчас поигрался, вижу что с рекордами в этом отношении всё отлично: можно объявлять свойства, по ним работаем паттерн-матчинг (только простые кейсы попробовал, буду надеяться, что работает всё).
С вариантами такое не прокатывает. Но подробнее. Рассмотрим сначала такой вот вариант:
variant Color
{
| Red
| Green
| Blue
}
Из него получается
internal abstract class Color
{
// Methodsprotected Color();
public abstract override int _N_GetVariantCode();
public static int _N_GetVariantCodeObject(object x);
public static int _N_GetVariantCodeSafe(Color x);
internal protected sealed class Blue : Color { }
…
Если бы мне пришлось выписывать вариант "руками", то я сделал бы немного иначе:
Приватный конструктор был бы более к месту: все наследники inner classes. Сейчас, как я понимаю, запрет наследования стороннего класса достигается специальной проверкой в компиляторе. Она окажется не нужной.
Не лучше ли дать нормальные имена методам, без префиксов "_N_" и, если нужно, какие-то специальные методы почечать атрибутами?
Сделать NVI: protected abstract int GetVariantCodeCore(); + public int GetVariantCode();
Далее. Дополняем вариант:
variant Color
{
| Red
| Green
| Blue
| Custom {
Red : int
}
}
и получаем:
Warning: field: Red : int; hides 'Color.Red' but neither 'override' nor 'new' is specified
Warning: hint: hiden definition
Ничто не мешает компилятору самостоятельно добавить "new" к объявлению поля. То, что вариант реализуется посредством наследования можно вообще считать деталью реализации компилятора.
Теперь делаем так:
variant Color
{
| Red
| Green
| Blue
| Custom {
Red : int{ get; private set; }
}
}
module Program
{
W() : Color {
Color.Custom(1)
}
Main() : void
{
def color = W();
match(color) {
| Color.Red => WriteLine("Red")
| Color.Green => WriteLine("Green")
| Color.Blue => WriteLine("Blue")
| Color.Custom(red) => WriteLine("Red: {0}", red)
}//match_ = ReadLine();
}
}
И это не компилируется:
Error: pattern matches 1 values, while the type 'Color.Custom' has 0 fields
Error: unbound name 'red'
Сложно исправить?
Кстати, почему, если написать код вот так:
module Program
{
Main() : void
{
def color = Color.Custom(1);
match(color) {
| Color.Red => WriteLine("Red")
| Color.Green => WriteLine("Green")
| Color.Blue => WriteLine("Blue")
| Color.Custom(red) => WriteLine("Red: {0}", red)
}//match
}
}
public variant Color
{
| Red
| Green
| Blue
| Custom {
Red : int
}
}
то получаем целый ворох ошибок:
Error: the matched value type Color.Custom was expected to be compatible with Color.Red: Color.Red is not a subtype of Color.Custom [simple require]
Error: the matched value type Color.Custom was expected to be compatible with Color.Green: Color.Green is not a subtype of Color.Custom [simple require]
Error: the matched value type Color.Custom was expected to be compatible with Color.Blue: Color.Blue is not a subtype of Color.Custom [simple require]
Warning: this match clause is unused
Warning: this match clause is unused
Warning: this match clause is unused
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Если бы мне пришлось выписывать вариант "руками", то я сделал бы немного иначе: _FR>
_FR>Приватный конструктор был бы более к месту: все наследники inner classes. Сейчас, как я понимаю, запрет наследования стороннего класса достигается специальной проверкой в компиляторе. Она окажется не нужной. _FR>Не лучше ли дать нормальные имена методам, без префиксов "_N_" и, если нужно, какие-то специальные методы почечать атрибутами? _FR>Сделать NVI: protected abstract int GetVariantCodeCore(); + public int GetVariantCode(); _FR>
Защищенный конструктор — это по-видимому просто недоделка. Думаю легко можно поправить на приватный .
_FR>Ничто не мешает компилятору самостоятельно добавить "new" к объявлению поля. То, что вариант реализуется посредством наследования можно вообще считать деталью реализации компилятора.
Не вижу проблемы в этом сообщении компилятора.
_FR>И это не компилируется: _FR>
_FR>Error: pattern matches 1 values, while the type 'Color.Custom' has 0 fields
_FR>Error: unbound name 'red'
_FR> Сложно исправить?
Да. Теперь объясни политику матча таких вещей:
variant Foo
{
| Baz
| Bar
{
public X { get; }
public Y { get { X + 1 } }
}
}
В любом случае такие изменения сейчас в компилятор внедрять не будем.
Здравствуйте, hardcase, Вы писали:
_FR>>Ничто не мешает компилятору самостоятельно добавить "new" к объявлению поля. То, что вариант реализуется посредством наследования можно вообще считать деталью реализации компилятора. H>Не вижу проблемы в этом сообщении компилятора.
А, понятно.
_FR>> Сложно исправить? H>Да. Теперь объясни политику матча таких вещей:
H>variant Foo
H>{
H> | Baz
H> | Bar
H> {
H> public X { get; }
H> public Y { get { X + 1 } }
H> }
H>}
Что такое "public X { get; }" без сеттера? Имеется в виду { get; private set; } как у меня?
Во-вторых, как я понимаю, в паттерне Color.Custom(red) "red" матчится по параметру конструктора, то есть в этой форме X будет матчится понятно как, а Y, наверное, никак. А сейчас в Немерлое что, нельзя заматчить самый обычный объект по его свойствам? Синтаксис такого матча придумать не очень сложно.
В-третьих: X в данном случае матчиться может. Я не просил добавлять возможность матча по Y в примере. Я прошу возможность матча по X. А такая возможность есть, потому что компилятор знает о зависимости свойства X и первого параметра образца.
H>В любом случае такие изменения сейчас в компилятор внедрять не будем.
А, понятно.
_FR>>Кстати, почему, если написать код вот так: _FR>>[nemerle] _FR>>module Program _FR>>{ _FR>> Main() : void _FR>> { _FR>> def color = Color.Custom(1); _FR>> match(color) { _FR>> | Color.Red => WriteLine("Red") _FR>> | Color.Green => WriteLine("Green") _FR>> | Color.Blue => WriteLine("Blue") _FR>> | Color.Custom(red) => WriteLine("Red: {0}", red) _FR>> }//match _FR>> } _FR>>}
H>Потому что тип color : Color.Custom и потому его бессмысленно матчить на Color.Red, Color.Green и Color.Blue.
В таком случае компиляторы обычно выдают одно предупреждение (ну лано, тут можно и схалтурить и выдать три ворнинга), а не три ошибки + три предупреждения
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Что такое "public X { get; }" без сеттера? Имеется в виду { get; private set; } как у меня?
Это get-only свойство, Nemerle дозволяет их объявлять.
_FR>Во-вторых, как я понимаю, в паттерне Color.Custom(red) "red" матчится по параметру конструктора, то есть в этой форме X будет матчится понятно как, а Y, наверное, никак. А сейчас в Немерлое что, нельзя заматчить самый обычный объект по его свойствам? Синтаксис такого матча придумать не очень сложно.
Можно — через оператор where.
_FR>В-третьих: X в данном случае матчиться может. Я не просил добавлять возможность матча по Y в примере. Я прошу возможность матча по X. А такая возможность есть, потому что компилятор знает о зависимости свойства X и первого параметра образца.
H>>В любом случае такие изменения сейчас в компилятор внедрять не будем.
_FR>А, понятно.
Это чисто технически сложно (много кода нужно править), к ближайшему релизу (кстати, где он?) делать не будем, а в качестве рефреша (Nemerle 1.1) я думаю можно.
_FR>>>Кстати, почему, если написать код вот так: _FR>>>[nemerle] _FR>>>module Program _FR>>>{ _FR>>> Main() : void _FR>>> { _FR>>> def color = Color.Custom(1); _FR>>> match(color) { _FR>>> | Color.Red => WriteLine("Red") _FR>>> | Color.Green => WriteLine("Green") _FR>>> | Color.Blue => WriteLine("Blue") _FR>>> | Color.Custom(red) => WriteLine("Red: {0}", red) _FR>>> }//match _FR>>> } _FR>>>}
H>>Потому что тип color : Color.Custom и потому его бессмысленно матчить на Color.Red, Color.Green и Color.Blue.
_FR>В таком случае компиляторы обычно выдают одно предупреждение (ну лано, тут можно и схалтурить и выдать три ворнинга), а не три ошибки + три предупреждения
Здравствуйте, hardcase, Вы писали:
_FR>>Что такое "public X { get; }" без сеттера? Имеется в виду { get; private set; } как у меня? H>Это get-only свойство, Nemerle дозволяет их объявлять.
О как.
Кстати, при отсутствии явно заданного пользователем конструктора для типа с таким свойством я бы генерил два конструктора: один по-умолчанию, другой: в котором на каждое такое свойство есть параметр со значением по-умолчанию.
_FR>>Во-вторых, как я понимаю, в паттерне Color.Custom(red) "red" матчится по параметру конструктора, то есть в этой форме X будет матчится понятно как, а Y, наверное, никак. А сейчас в Немерлое что, нельзя заматчить самый обычный объект по его свойствам? Синтаксис такого матча придумать не очень сложно. H>Можно — через оператор where.
Тогда не понимаю, что мешает реализовать матчинг для Bar X матчится как параметр конструктора, Y через where
_FR>>В-третьих: X в данном случае матчиться может. Я не просил добавлять возможность матча по Y в примере. Я прошу возможность матча по X. А такая возможность есть, потому что компилятор знает о зависимости свойства X и первого параметра образца. H>>>В любом случае такие изменения сейчас в компилятор внедрять не будем. _FR>>А, понятно. H>Это чисто технически сложно (много кода нужно править), к ближайшему релизу (кстати, где он?) делать не будем, а в качестве рефреша (Nemerle 1.1) я думаю можно.
Если дело просто в сложности — это одно. Главное, что бы было понятие о нужности.
_FR>>>>Кстати, почему, если написать код вот так:
H>>>Потому что тип color : Color.Custom и потому его бессмысленно матчить на Color.Red, Color.Green и Color.Blue. _FR>>В таком случае компиляторы обычно выдают одно предупреждение (ну лано, тут можно и схалтурить и выдать три ворнинга), а не три ошибки + три предупреждения H>Там warning-и точно лишние
Так почему это ошибка-то?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Кстати, при отсутствии явно заданного пользователем конструктора для типа с таким свойством я бы генерил два конструктора: один по-умолчанию, другой: в котором на каждое такое свойство есть параметр со значением по-умолчанию.
Зачем?
_FR>Тогда не понимаю, что мешает реализовать матчинг для Bar X матчится как параметр конструктора, Y через where
Тем что работа с одними и теми же сущностями — свойствами. Будет происходить различным образом. Одни свойства можно будет использовать в паттерне "конструктор", а другие — нет. Совместно их использовать можно будет лишь в паттерне "объект".
_FR>Если дело просто в сложности — это одно. Главное, что бы было понятие о нужности.
Вот кстати с нужностью и проблема Мне необходимость в таком изменении совсем не очевидна.
_FR>Так почему это ошибка-то?
Компилятор ясно выразился:
Error: the matched value type Color.Custom was expected to be compatible with Color.Red: Color.Red is not a subtype of Color.Custom [simple require]
Тип переменной color : Color.Custom, который не является супертипом Color.Red. Чтобы твой код заработал достаточно указать базовый тип в одном из двух мест:
1) я предпочитаю этот способ
def color = Color.Custom(1);
match(color : Color) {
| Red => WriteLine("Red")
| Green => WriteLine("Green")
| Blue => WriteLine("Blue")
| Custom(red) => WriteLine($"Red: $red")
}
либо
2)
def color : Color = Color.Custom(1);
match(color) {
| Red => WriteLine("Red")
| Green => WriteLine("Green")
| Blue => WriteLine("Blue")
| Custom(red) => WriteLine($"Red: $red")
}
Здравствуйте, hardcase, Вы писали:
_FR>>Кстати, при отсутствии явно заданного пользователем конструктора для типа с таким свойством я бы генерил два конструктора: один по-умолчанию, другой: в котором на каждое такое свойство есть параметр со значением по-умолчанию. H>Зачем?
Затем, что если в классе есть такое свойство и не определён конструктор, то установить значение такого свойства извне не представляется возможным. Или как-то можно?
_FR>>Тогда не понимаю, что мешает реализовать матчинг для Bar X матчится как параметр конструктора, Y через where H>Тем что работа с одними и теми же сущностями — свойствами. Будет происходить различным образом. Одни свойства можно будет использовать в паттерне "конструктор", а другие — нет. Совместно их использовать можно будет лишь в паттерне "объект".
Будет различными. И это более чем объяснимо: природа свойств различна: одно инициализируется через конструктор, другое нет.
_FR>>Если дело просто в сложности — это одно. Главное, что бы было понятие о нужности. H>Вот кстати с нужностью и проблема Мне необходимость в таком изменении совсем не очевидна.
А, понятно.
_FR>>Так почему это ошибка-то? H>Компилятор ясно выразился:
Ну в общем ясно.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Затем, что если в классе есть такое свойство и не определён конструктор, то установить значение такого свойства извне не представляется возможным. Или как-то можно?
Макрос Record генерирует конструктор, в котором учитываются автоматические свойства.
Здравствуйте, hardcase, Вы писали:
_FR>>Затем, что если в классе есть такое свойство и не определён конструктор, то установить значение такого свойства извне не представляется возможным. Или как-то можно?
H>Макрос Record генерирует конструктор, в котором учитываются автоматические свойства.
В нём учитываются, если я правильно понимаю, все свойства. Кстати, а Record не генерит ToString() если та не определена? В общем, Record — это скорее некая отдельная сущность и вот тут смешивать одно с другим совсем не к чему. Но это конечно же ваше дело.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
H>>Макрос Record генерирует конструктор, в котором учитываются автоматические свойства.
_FR>В нём учитываются, если я правильно понимаю, все свойства.
Record генерирует конструкторы, которые наследуют все конструкторы предка + добавляет параметры для полей и автоматических свойств.
_FR>Кстати, а Record не генерит ToString() если та не определена?