Ну что ж. В итоге после копания в компиляторе, разговоров с Москалем и крепких размышлений, пришел вот к чему:
Для написания оператора '??' одним if/else не обойтись. Ну второй if/else спецефичный для наллаблов, особого труда не представляет, так что я действительно понаворотил кучу ненужного кода, который по сути является заплатками недоработок компилятора.
Для нормального функционирования макроса нужно пофиксать 2 бага:
1. Хотя этот баг Москаль считает не багом, а скорее некоторым ограничением, он все равно планирует его поправить:
class A {}
class B
public static @:(_ : B) : A
A()
def a = A()
def b = B()
// expected B-, got A in computation branch:
def c = if (a==null) b else a
В if/match проверяется только возможность приведения к базовому типу с целью поиска общего типа, но не проверяется возможность implicit приведения.
2. Баг с implicit/explicit приведением generic типов:
def p = (10 : int?) :> int;
// это был пример с наллаблами, но проблема распростроняется и на все другие типы, например:class Y['t]
mutable _t : 't
public this(t : 't)
_t = t
public static @:> (y : Y['t]) : 't
y._t
def y = Y.[int](10)
def x = y :> int
С этой проблемой я решил попробовать разобраться сам. Сразу скажу ничерта не вышло, но в основной задаче, которую я себе ставил — понять как работает компилятор Немерла — маленько продвинулся.
В классе Typer есть метод TypeConversion. Который проверяет можно ли expression привести к некоторому типу. Так вот что этот метод делает касательно приведенного выше примера:
а. получает список всех implicit и explicit операторов у типа expression и приводимого типа;
б. выкидывает повторяющиеся;
в. строит needed — тип функции, с сигнатурой требуемой для нашего преобразования (Y[int] -> int);
г. проверяет имеют ли implicit/explicit операторы сигнатуру эквивалентную needed, если нет, выкидываем из рассмотрения;
д. выбираем наилучший оператор, используем его.
Так вот проблема возникла в пункте г. op_Explicit для приведенного примера имеет сигнатуру Y['t] -> 't, а не как нам бы хотелось Y[int] -> int. Я написал патч строящий правильные сигнатуры (в мантисе в баге я этот патч приаттачил), но это не решило проблему. Начал валиться с исключением Typer4:
tv 't.836 defined in Y and accessed from _N_AutoModule
Причина в принципе ясна, а вот как ее пофиксать пока не предумал. Вообщем решил запостить эту багу. Будет время, продолжу ознакомнение с компилятором и пофиксаю какую-нибудь другую багу в следующий раз
Хмммм...... Дописал все и думаю, а нафига я все это пишу....
Ну уж коль написал пущай будет. Мож кому интересно...
Вот это дело нужно в статические переменные отдельного скрытого модуля вынести. Не фига их при каждой компиляции создавать.
А эти: ie> def refEq = System.Type.ReferenceEquals; ie> def ref3Eq = (t1,t2,t3) => refEq(t1, t2) || refEq(t1, t3);
Лучше переписать на обычных локальных методах чтобы вызова по ссыне не блыло. Хотя это уже не так важно.
Так как оператор вызвается довольно часто стоит немного подоптимизировать.
И вот это:
<[ if ($exprA.IsSome) $exprA else $exprB ]>;
мне кажется ошибкой. Ведь $exprA будет тоже типа Some(...), а это не верно. Надо так:
<[ match ($exprA) { | Some(x) => x | _ => $exprB ]>;
Ну, и соотвественно тесты надо исправить. А то ты их явно под ошибку подстрогал.
Не:
Потом match по двум значениям (кортежу) медленее чем по одному значению. А так как у тебя в первом элементе всегда Some (Class (tiA, _)), то стоит использовать вложенный match. Оно и понятнее будет.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Кстати, меня интересует вопрос... Ты вот сравниваешь дотнетные типы основываясь на том, что все нужные тебе типы всегда определены во вне. А как быть если нужно сравнить типы которые могут быть определены как во внешней сборке, так и в компилируемом проекте?
Может есть какой-то более универсальный способ?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Вот это дело нужно в статические переменные отдельного скрытого модуля вынести. Не фига их при каждой компиляции создавать.
Это да. Добавлю нужные типы в \ncc\external\internaltypes.n
VD>И вот это: VD>
VD> <[ if ($exprA.IsSome) $exprA else $exprB ]>;
VD>
VD>мне кажется ошибкой. Ведь $exprA будет тоже типа Some(...), а это не верно. Надо так: VD>
VD> <[ match ($exprA) { | Some(x) => x | _ => $exprB ]>;
VD>
Тут я сделал по аналогии с nullable. A ?? B. Если A и B nullable, то результат тоже nullable, если A nullable, а B нет, то и результат является базой nullable. Ты привел кусок кода, где оба выражения option. В этом случае и результат option.
VD>Потом match по двум значениям (кортежу) медленее чем по одному значению. А так как у тебя в первом элементе всегда Some (Class (tiA, _)), то стоит использовать вложенный match. Оно и понятнее будет.
Здравствуйте, VladD2, Вы писали:
VD>Кстати, меня интересует вопрос... Ты вот сравниваешь дотнетные типы основываясь на том, что все нужные тебе типы всегда определены во вне. А как быть если нужно сравнить типы которые могут быть определены как во внешней сборке, так и в компилируемом проекте?
Что-то не понял вопроса. Или ты имеешь ввиду типы, которые могут появиться в компайл-тайме после выполнения какого-то макроса?
VD>> <[ if ($exprA.IsSome) $exprA else $exprB ]>;
VD>>
VD>>мне кажется ошибкой. Ведь $exprA будет тоже типа Some(...), а это не верно. Надо так: VD>>
VD>> <[ match ($exprA) { | Some(x) => x | _ => $exprB ]>;
VD>>
ie>Тут я сделал по аналогии с nullable. A ?? B. Если A и B nullable, то результат тоже nullable, если A nullable, а B нет, то и результат является базой nullable. Ты привел кусок кода, где оба выражения option. В этом случае и результат option.
Но это бессмысленно! Даже больше. Это просто неразумно. Да и работать не может. Ведь не может же один и тот же оператор в разные моменты возвращать Nullable[T] и T?
В общем, это явный баг.
Весь смысл этого оператора в том, чтобы он возвращал значение засунутое в nullable тип или значение используемое по умолчанию.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ie, Вы писали:
ie>Здравствуйте, VladD2, Вы писали:
VD>>Кстати, меня интересует вопрос... Ты вот сравниваешь дотнетные типы основываясь на том, что все нужные тебе типы всегда определены во вне. А как быть если нужно сравнить типы которые могут быть определены как во внешней сборке, так и в компилируемом проекте?
ie>Что-то не понял вопроса. Или ты имеешь ввиду типы, которые могут появиться в компайл-тайме после выполнения какого-то макроса?
На самом деле я уже нашел вопрос. Сравнивать нужно не дотнетные типы (т.е. не System.Type), а Nemerle-овское TypeInfo. Немерловцы сами создали список часто используемых типов и заполняют его в начальной стадии компиляции. Правда туда можно поместить только внешние типы. А внутренние нужно "лукапить" перед использованием.
Но проблемы все же остаются. В рефлекшоне есть фунукции позволяющие узнать является ли тип подтипом другого типа, или реализует ли тип некоторый интерйесй. Похоже подобных функций в Немерле попросту нет. Так что их нужно еще написать. А жаль.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Но это бессмысленно! Даже больше. Это просто неразумно. Да и работать не может. Ведь не может же один и тот же оператор в разные моменты возвращать Nullable[T] и T? VD>В общем, это явный баг.
VD>Весь смысл этого оператора в том, чтобы он возвращал значение засунутое в nullable тип или значение используемое по умолчанию.
Однока, спецификация C# так не считает, и в этом я с ней солидарен.
int? p = null;
int? q = null;
if (p ?? q == 0); // тут мне 0 нафиг не нужен
Здравствуйте, IT, Вы писали:
ie>>А правильно ли я понимаю, что он будет иметь поведение другое нежели '??'. Например: IT>Если выражать через ??, то поведение должно быть примерно такое:
IT>
IT>mutable a;
IT>def b = a ?? { a = c; a };
IT>
Сравнивая поведение, я имел ввиду немного другое.
mutable a : int? = null;
def b = a ?? 5; // тут тип int, а если тупо выражать ??= через ??, то получим бредовые ошибки.
Здравствуйте, VladD2, Вы писали:
ie>>Что-то не понял вопроса. Или ты имеешь ввиду типы, которые могут появиться в компайл-тайме после выполнения какого-то макроса?
VD>На самом деле я уже нашел вопрос. Сравнивать нужно не дотнетные типы (т.е. не System.Type), а Nemerle-овское TypeInfo. Немерловцы сами создали список часто используемых типов и заполняют его в начальной стадии компиляции. Правда туда можно поместить только внешние типы. А внутренние нужно "лукапить" перед использованием.
Я собственно в итоге все на TypeInfo и поменял.
VD>Но проблемы все же остаются. В рефлекшоне есть фунукции позволяющие узнать является ли тип подтипом другого типа, или реализует ли тип некоторый интерйесй. Похоже подобных функций в Немерле попросту нет. Так что их нужно еще написать. А жаль.
Ну BaseType то есть, а значит как минимум IsSubclassOf или IsAssignableFrom достаточно просто реализуемы.
Здравствуйте, IT, Вы писали:
ie>>def b = a ?? 5; // тут тип int, а если тупо выражать ??= через ??, то получим бредовые ошибки. IT>Какие именно бредовые? Если я буду делать тоже самое без макроса, то ошибки будут не бредовые?
Я про ошибки компилятора:
Error 1 expected option[int-]-, got int in assigned value: common super type of types [option[int-], int] is just `System.Object', please upcast one of the types to `System.Object' if this is desired E:\Ivan\Projects\MyOwn Projects\Nemerle Projects\Macro\Cns.MacroLibrary.Test\Main.n 78 9 Cns.MacroLibrary.Test
Error 2 there is no member named `HasValue' in System.Object- with type ? E:\Ivan\Projects\MyOwn Projects\Nemerle Projects\Macro\Cns.MacroLibrary.Test\Main.n 78 9 Cns.MacroLibrary.Test
Error 3 there is no member named `Value' in System.Object- with type ? E:\Ivan\Projects\MyOwn Projects\Nemerle Projects\Macro\Cns.MacroLibrary.Test\Main.n 78 9 Cns.MacroLibrary.Test
Здравствуйте, ie, Вы писали:
ie>>>def b = a ?? 5; // тут тип int, а если тупо выражать ??= через ??, то получим бредовые ошибки. IT>>Какие именно бредовые? Если я буду делать тоже самое без макроса, то ошибки будут не бредовые?
ie>Я про ошибки компилятора:
И я про тоже.
ie>А по уму бы написать: ie>
ie>`5' should have type `option[int]'.
Так было бы совсем хорошо. Но как я понимаю, компилятору всё равно делается ли что-то неверно из макроса или прямо в коде. Ошибки будут одни и те же.
Конечно, лучше выдавать вменяемые ошибки. А ещё лучше, сделать самому приведение, если это возможно.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
ie>>А по уму бы написать: ie>>
ie>>`5' should have type `option[int]'.
IT>Так было бы совсем хорошо. Но как я понимаю, компилятору всё равно делается ли что-то неверно из макроса или прямо в коде. Ошибки будут одни и те же.
Если так написать:
def b = {when (a == null) a = 6; a};
, то хотя бы не будет сообщений про HasValue/Value. Хотя эти сообщение спецефично для option.
IT>Конечно, лучше выдавать вменяемые ошибки.
+100, а то эти невменяемые отмазки компилятора уже напрягают.
IT>А ещё лучше, сделать самому приведение, если это возможно.
Хмм. Какими правилами руководствоваться? Implicit и UpCast?