Не так давно IT в Nemerle'овской рассылке спрашивал про оператор ??, который есть в Шарпе и напрочь отсутствует в Nemerle. Ну я и решлил для расзимнки написать его макросом. Оказалось не все так просто
Сперва, приведу соответствующий кусок C# спецификации:
14.12 The null coalescing operator
The ?? operator is called the null coalescing operator. null-coalescing-expression:
conditional-or-expression
conditional-or-expression ?? null-coalescing-expression A null coalescing expression of the form a ?? b requires a to be of a nullable type or reference type. If a is
non-null, the result of a ?? b is a; otherwise, the result is b. The operation evaluates b only if a is null.
The null coalescing operator is right-associative, meaning that operations are grouped from right to left.
[Example: An expression of the form a ?? b ?? c is evaluated as a ?? (b ?? c). In general terms, an
expression of the form E1 ?? E2 ?? ... ?? EN returns the first of the operands that is non-null, or null if all
operands are null. end example]
The type of the expression a ?? b depends on which implicit conversions are available between the types
of the operands. In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a, B is the
type of b, and A0 is the type that results from removing the trailing ? modifier, if any, from A. Specifically,
a ?? b is processed as follows:
• If A is not a nullable type or a reference type, a compile-time error occurs.
• If A is a nullable type and an implicit conversion exists from b to A0, the result type is A0. At run-time, a
is first evaluated. If a is not null, a is unwrapped to type A0, and this becomes the result. Otherwise, b is
evaluated and converted to type A0, and this becomes the result.
• Otherwise, if an implicit conversion exists from b to A, the result type is A. At run-time, a is first
evaluated. If a is not null, a becomes the result. Otherwise, b is evaluated and converted to type A, and
this becomes the result.
• Otherwise, if an implicit conversion exists from A0 to B, the result type is B. At run-time, a is first
evaluated. If a is not null, a is unwrapped to type A0 (unless A and A0 are the same type) and converted
to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.
Otherwise, a and b are incompatible, and a compile-time error occurs.
Решил делать все по уму, а именно стараться неприкоснительно следовать спецификации. Итак, вот такой вот макрос получился:
using System.Console;
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Compiler.Typedtree;
using Nemerle.Text;
namespace Iae.Macro
{
/** This module is used to simplify work with op_Implicit operator.
This module is stateless.
*/module OpImplicitHelper
{
/**
* Used to print all implicit operators of some type
*/public PrintAll(cl : MType.Class) : void
{
def members = cl.tycon.LookupMember("op_Implicit");
foreach (m is IMethod in members)
WriteLine(m);
}
/**
* Used to determine has a `from' type implicit conversation
* to a `to' type or hasn't.
*/public HasImplicit(from : MType.Class, to : MType.Class) : bool
{
def members = to.tycon.LookupMember("op_Implicit")
+ from.tycon.LookupMember("op_Implicit");
ret :
{
foreach (m is IMethod in members)
{
def tyRet = m.ReturnType;
def tyPar = m.GetParameters().Head.ty;
when (tyRet.Equals(to) && tyPar.Equals(from))
ret (true);
}
false;
}
}
}
macro @printImplicit(expr)
{
def typer = Macros.ImplicitCTX ();
def tx = typer.TypeExpr (expr);
match (tx.Type.Hint)
{
| Some (Class (_ti, _args) as cl) =>
OpImplicitHelper.PrintAll(cl);
<[]>
| _ =>
Message.FatalError ("PrintImplicit macro can not be used with such expression.");
}
}
macro @test(expr)
{
<[ if ($expr != null) $expr.Value; else (CI () : int) ]>
}
macro @?? (exprA, exprB)
{
def typer = Macros.ImplicitCTX ();
def tx = (typer.TypeExpr(exprA).Type.Hint, typer.TypeExpr(exprB).Type.Hint);
def teA = typer.TypeExpr(exprA);
def teB = typer.TypeExpr(exprB);
def tyA = typer.TypeExpr(exprA).Type;
def tyB = typer.TypeExpr(exprB).Type;
def upCast =
(texpr, ty) =>
TExpr.TypeConversion (texpr.loc, ty, texpr, ty,
ConversionKind.UpCast());
def _implicit =
(texpr, ty) =>
TExpr.TypeConversion (texpr.loc, ty, texpr, ty,
ConversionKind.Implicit());
match (tx)
{
| (Some(Class(tiA, [tyA0]) as clA), Some(Class(_, _) as clB))
when tiA.IsValueType && tiA.FullName == "System.Nullable" =>
def clA0 = tyA0 :> MType.Class;
if (clA0.Equals(clB)
|| OpImplicitHelper.HasImplicit(clB, clA0))
{
/**
* BUG: 'Nullable[T]' can not be converted to 'T',
* but it should.
* Example:
* def inull : int? = 10
* // cannot convert System.Nullable[int] to int:
* def i : int = inull :> int
**/
def st = <[
if ($exprA != null)
$exprA.Value
else
$(upCast(teB, tyA0) : typed)
]>;
WriteLine($"nullable: B -> A0: $st");
st
}
else if (clA.Equals(clB)
|| OpImplicitHelper.HasImplicit(clB, clA))
{
/**
* BUG: 'Nullable[T1]' can not be converted to
* 'Nullable[T2]' when 'T1' has implicit
* conversation to 'T2', but it should.
* C# Example:
* int? i = 10;
* double? d = i;
**/
def st = <[
if ($exprA != null)
$exprA
else
$(upCast(teB, tyA) : typed)
]>;
WriteLine($"nullable: B -> A: $st");
st
}
else if (OpImplicitHelper.HasImplicit(clA0, clB))
{
/**
* BUG: 'T1' can not be converted to 'Nullable[T2]'
* when 'T1' has implicit conversation to 'T2',
* but it should.
* C# Example:
* int i = 10;
* double? d = 0.1;
* d = i;
**/
def st = <[
if ($exprA != null)
$(upCast(typer.TypeExpr(<[ $exprA.Value ]>), tyB) : typed)
else
$exprB
]>;
WriteLine($"nullable: A0 -> B: $st");
st
}
else
{
Message.FatalError ($"Operator `??' cannot be applied to "
"operands of type `$clA' and `$clB'");
}
| (Some(Class(tiA, _)), Some(Class(_, _)))
when tiA.IsValueType =>
Message.FatalError (exprA.Location,
$"`$tiA' is not a reference or nullable type "
"as required by the `??' operator");
| (Some(Class(_, _) as clA), Some(Class(_, _) as clB))
when clA.Equals(clB) || OpImplicitHelper.HasImplicit(clB, clA) =>
/**
* ?BUG: This match case and next one can be replaced with single
* one, but followed by code doesn't compile. Is this a design
* decision or a bug? (C# compile such code with no problems)
* Example:
* #pragma indent
* 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
**/
def st = <[
if ($exprA != null)
$exprA
else
$(upCast(teB, tyA) : typed)
]>;
WriteLine($"reference: B -> A: $st");
st
| (Some(Class(_, _) as clA), Some(Class(_, _) as clB))
when OpImplicitHelper.HasImplicit(clA, clB) =>
def st = <[
if ($exprA != null)
$(upCast(teA, tyB) : typed)
else
$exprB
]>;
WriteLine($"reference: A -> B: $st");
st
| _ =>
Message.FatalError ($"Operator `??' cannot be applied to "
"operands of type `$tyA' and `$tyB'");
}
}
}
В процессе написания понял, что в Nemerle работа с Nullable типами просто никакая. В коде макроса в комментах можно встретить баги Nullable типов, которые мне с ходу удалось обнаружить. Чуть позже все баги занесу в багтрэкер, но прежде хочу разобраться в ряде моментов, ибо есть стойкое ощущение, что чего-то я недопонимаю.
Итак, пример использования макроса, с комментариями проблем:
#pragma indent
using System.Console;
using Iae.Macro;
class C1
public override ToString() : string"C1"public Test() : void
WriteLine("C1::Test");
class C2
public static @:(_ : C2) : C1
C1()
public override ToString() : string"C2"class CI
public static @:(_ : CI) : int
2
def getType['t] (_ : 't)
$"$(typeof('t))"
def c1 = null : C1
def c2 = C2()
def r1 = c1 ?? c2 // (***)compile-time message: "reference: B -> A: if (c1 != null) c1; else (c2 : C1)"
def c1 = C1()
def t1 = getType(r1)
WriteLine($"TEST REFERENCE B -> A (result): $r1")
WriteLine($"TEST REFERENCE B -> A (type): $t1")
// run-time messages:
// TEST REFERENCE B -> A (result): C2
// TEST REFERENCE B -> A (type): C1
def r2 = c2 ?? c1 // compile-time message: "reference: A -> B: if (c2 != null) (c2 : C1); else c1"
def t2 = getType(r2)
WriteLine($"TEST REFERENCE A -> B (result): $r2")
WriteLine($"TEST REFERENCE A -> B (type): $t2")
// run-time messages:
// TEST REFERENCE B -> A (result): C2
// TEST REFERENCE B -> A (type): C1
def r0 = if (c1 == null) (c2 : C1) else c1 // собственно, тоже самое, что и в (***)
def t0 = getType(r0)
WriteLine($"TEST REFERENCE B -> A (result): $r0")
WriteLine($"TEST REFERENCE B -> A (type): $t0")
// run-time messages: выводит то, что ожидается в предыдущих случаях
// TEST REFERENCE B -> A (result): C1
// TEST REFERENCE B -> A (type): C1
def inull_v : int? = 10
def inull_n : int? = null
def r3 = inull_v ?? 1 // compile-time message: "nullable: B -> A0: if (inull_v != null) inull_v.Value; else (1 : int)"
def t3 = getType(r3)
WriteLine($"TEST NULLABLE B -> A0 (result): $r3")
WriteLine($"TEST NULLABLE B -> A0 (type): $t3")
// run-time messages:
// TEST NULLABLE B -> A0 (result): 10
// TEST NULLABLE B -> A0 (type): System.Int32
def r4 = inull_n ?? 1 // compile-time message: "nullable: B -> A0: if (inull_n != null) inull_v.Value; else (1 : int)"
def t4 = getType(r4)
WriteLine($"TEST NULLABLE B -> A0 (result): $r4")
WriteLine($"TEST NULLABLE B -> A0 (type): $t4")
// run-time messages:
// TEST NULLABLE B -> A0 (result): 1
// TEST NULLABLE B -> A0 (type): System.Int32
def r5 = test(inull_n) // это макрос, см. выше
def t5 = getType(r5)
WriteLine($"TEST NULLABLE B -> A0 (result): $r5")
WriteLine($"TEST NULLABLE B -> A0 (type): $t5")
// run-time messages:
// TEST NULLABLE B -> A0 (result): 2
// TEST NULLABLE B -> A0 (type): System.Int32
def r5 = inull_n ?? CI() // compile-time message: "nullable: B -> A0: if (inull_n != null) inull_n.Value; else (CI () : int)"
// Compile-time exception:
//test.n:59:21:59:23: debug: Internal compiler error, please report a bug to bugs.nemerle.org. You can try modifying program near this location.
//[01;31merror[0m: internal compiler error: assertion failed in file ncc\generation\ILEmitter.n, line 801:
//_N_AutoModule::Main: failed to convert non-value type CI to a value type int
// .............
def r5 = if (inull_n != null) inull_n.Value; else (CI () : int)
def t5 = getType(r5)
WriteLine($"TEST NULLABLE B -> A0 (result): $r5")
WriteLine($"TEST NULLABLE B -> A0 (type): $t5")
// run-time messages:
// TEST NULLABLE B -> A0 (result): 2
// TEST NULLABLE B -> A0 (type): System.Int32
Вообщем сдается мне, что есть какие-то тонкости, о которых я пока не знаю... Может есть какие-то идеи, почему макрос отрабатывает не так как хотелось бы?
... << RSDN@Home 1.2.0 alpha rev. 655>>
30.01.07 18:10: Перенесено модератором из 'Декларативное программирование' — IT
Здравствуйте, PhantomIvan, Вы писали:
ie>>В процессе написания понял, что в Nemerle работа с Nullable типами просто никакая. PI>это определенно связано с наличием в языке option['a]
Здравствуйте, ie, Вы писали:
ie>Здравствуйте, PhantomIvan, Вы писали:
ie>>>В процессе написания понял, что в Nemerle работа с Nullable типами просто никакая. PI>>это определенно связано с наличием в языке option['a]
ie>Никакой связи не вижу...
превратить переменную в null — это все равно что показать, что в ней нет никакого осмысленного значения
в c# value-типы не имели возможности обратиться в null, и не имеют ее сейчас, т.к. реально при декларировании чего-то вроде int? создается объект класса вроде Nullable<int>
по идее, производительность, для которой собственно, и существуют value-типы, падает в результате применения фактически ссылочного типа вместо исходного value-типа
в немерле появляется класс option['a], который принимает 2 значения, аналогично nullable типу выше: Some(x) или None()
то есть это то же самое фактически, но семантика выражена более явно, при этом собственно null не используется, что дает возможность применять option для обычных reference-объектов
я пользовался и nullable-типами c#-а, и option из немерле
однако пользовался не много, и сказать что лучше, не могу
по-моему те же яйца, только в профиль
говорят, nullable-типы в c#-пе были нужны в связи с базаданными типами
и говорят также, что ado.net 2.0 реально их не использует
впрочем, мне все равно, что использовать из этих двух вещей — ведь по сути что там, что здесь это вариант по сути
а есть ли у option оверхед (синтаксический или перфоманс), — непонятно
может, со временем, увижу неудобства, и мое мнение изменится
PI>впрочем, мне все равно, что использовать из этих двух вещей — ведь по сути что там, что здесь это вариант по сути
все равно с точки зрения производительности
а иногда структуры становятся классами, и наоборот (рефакторинг типа)
и тогда, кажется, option более удобен, т.к. позволяет пустить всех под 1 гребенку
Здравствуйте, PhantomIvan, Вы писали:
PI>превратить переменную в null — это все равно что показать, что в ней нет никакого осмысленного значения
С одной стороны да, а с другой — наллаблы были введены как раз для унификации reference и value типов с точки зрения null-значений.
PI>в c# value-типы не имели возможности обратиться в null, и не имеют ее сейчас, т.к. реально при декларировании чего-то вроде int? создается объект класса вроде Nullable<int> PI>по идее, производительность, для которой собственно, и существуют value-типы, падает в результате применения фактически ссылочного типа вместо исходного value-типа
Прошу обратить внимание на выделенное:
publicstruct Nullable<T> where T: struct
Так что о падении производительности из-за использовании ссылок я бы смелых заявлений делать не стал бы
PI>в немерле появляется класс option['a], который принимает 2 значения, аналогично nullable типу выше: Some(x) или None() PI>то есть это то же самое фактически, но семантика выражена более явно, при этом собственно null не используется, что дает возможность применять option для обычных reference-объектов
Однако, повторюсь, наллаблы добавили для возможности работать со всеми объектами (в том числе и value-типов), как с объектами принимающими значение null.
Some же пришло в Nemerle из функционального мира, где значения null не существует. Это более высокий уровень абстракции.
IT>Как один из языков .NET Nemerle должен поддерживать всё, что есть в CLR.
тоже в принципе согласен
но это дается ценой некоторой путаницы, к примеру
коллекции .net — коллекции nemerle
nullable — option
delegates — function values
что применять, где применять, это путает
тех, кто только принимается программировать на немерле
Насколько я помню в рассылке упоминалось, что поддержка нулабл-типов ограниченная. Ребятам было просто влом терять на них время.
Откровенно говоря я сам так ни разу ими и не воспользовался по делу. Один раз захотелось, но обломался, так как они несовместимы со ссылочными типами.
Что до оператора ??, то больно сложно у тебя что-то выходит. Мне кажется ты перемудрил. Ничего осмысленного о реализации сказать не могу (не вникал).
Зато могу дать предложение. Хорошо бы чтобы этот оператор поддерживал бы и option[T].
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, PhantomIvan, Вы писали:
PI>тоже в принципе согласен PI>но это дается ценой некоторой путаницы, к примеру PI>коллекции .net — коллекции nemerle PI>nullable — option PI>delegates — function values
Делегаты обратно соместимы с функциональными объектами, так что все ОК.
Думаю, что и option можно сделать не отличимым в использовании от nullable.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, PhantomIvan, Вы писали:
PI>>тоже в принципе согласен PI>>но это дается ценой некоторой путаницы, к примеру PI>>коллекции .net — коллекции nemerle PI>>nullable — option PI>>delegates — function values
VD>Делегаты обратно соместимы с функциональными объектами, так что все ОК.
VD>Думаю, что и option можно сделать не отличимым в использовании от nullable.
но путаница возникает все-таки
вот, у Ильи кажется заминка с применением делагатов вышла
я к тому, что если некоторые вещи удобней в немерле (например функции), чем в дотнете (делегаты), то нужны ли, по большому счету, эти чисто дотнетные вещи...
Здравствуйте, PhantomIvan, Вы писали:
PI>но путаница возникает все-таки PI>вот, у Ильи кажется заминка с применением делагатов вышла PI>я к тому, что если некоторые вещи удобней в немерле (например функции), чем в дотнете (делегаты), то нужны ли, по большому счету, эти чисто дотнетные вещи...
Конечно. Причем просто для совместимости с другими языками дотнета. Почему-то меня не покидает уверенность, что Немерле еще не скоро станет едиственным или хотя бы основным языком дотнета. А до тех пор он будет прекрасным средством реализации длл-лек которые будут использоваться из других языков. И тут Немерле требуется все умение, чтобы успешно прикидываться C#-ом для внешних клиентов (т.е. создавать публичный интерфейс сборок не отличимый от C# последней версии).
В общем, с точки зрения возможности создания публичных интерфейсов и возможности использования чужих интерфейсов Nemerle обязан быть эдаким C#++, ну, или C##. Собственно это и декларируется.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, PhantomIvan, Вы писали:
PI>>но путаница возникает все-таки PI>>вот, у Ильи кажется заминка с применением делагатов вышла PI>>я к тому, что если некоторые вещи удобней в немерле (например функции), чем в дотнете (делегаты), то нужны ли, по большому счету, эти чисто дотнетные вещи...
VD>Конечно. Причем просто для совместимости с другими языками дотнета. Почему-то меня не покидает уверенность, что Немерле еще не скоро станет едиственным или хотя бы основным языком дотнета. А до тех пор он будет прекрасным средством реализации длл-лек которые будут использоваться из других языков. И тут Немерле требуется все умение, чтобы успешно прикидываться C#-ом для внешних клиентов (т.е. создавать публичный интерфейс сборок не отличимый от C# последней версии).
Правильней было бы все что нужно для прикидывания C# выделить в отдельную сущность (скажем библиотеку макросов — враперов) а не портить язык.
Здравствуйте, VladD2, Вы писали:
VD>Насколько я помню в рассылке упоминалось, что поддержка нулабл-типов ограниченная. Ребятам было просто влом терять на них время. VD>Откровенно говоря я сам так ни разу ими и не воспользовался по делу. Один раз захотелось, но обломался, так как они несовместимы со ссылочными типами.
Ну это в принципе ладно, не поддерживаются и бог с ними. Кстати, классная задачка, для желающего разобраться с компилятором (ну или хотя бы с его частью). Возможно, попробую пофиксать, коль время будет. Что до пользования ими, то мне реально пригодились наллаблы лишь несколько раз, примерно в таких случаях:
// без наллаблов:public bool GetValue(out int value)
{
if (надоВернутьЗначение)
{
value = 10;
return true;
}
else
{
value = 0;
return false;
}
}
int z;
if (GetValue(out z))
// do something
// с наллаблами:public int? GetValueN()
{
return надоВернутьЗначение ? 10 : (int?)null;
}
int? zn = GetValueN();
if (zn != null)
// do something
С наллаблами код становиться лаконичнее, хотя и без них обойтись можно.
VD>Что до оператора ??, то больно сложно у тебя что-то выходит. Мне кажется ты перемудрил. Ничего осмысленного о реализации сказать не могу (не вникал).
Дык, я тоже думал проще получится, а оказалось сам оператор достаточно служный и имеет ряд ньюансов. На самом деле все эти ньюансы касаются наллабл-типов. С референс-типами все сводится к банальному ?:, но и тут у Немерла проблемы:
C#:class A {}
class B : A {}
class C
{
public static implicit operator B(C c)
{
return new B();
}
}
A a = new A();
B b = new B();
C c = new C();
B b1 = c ?? b;
A a1 = c ?? a;
B b2 = c == null ? b : c;
A a2 = a == null ? c : a;
Nemerle:class A
;
class B : A
;
class C
public static @:>(c : C) : B
B();
def a = A();
def b = B();
def c = C();
// expected B-, got C in computation branch: common super type of types [B, C] is just `System.Object',
// please upcast one of the types to `System.Object' if this is desired
def b2 = if (c == null) b else c;
// expected C-, got A in computation branch: common super type of types [C, A] is just `System.Object',
// please upcast one of the types to `System.Object' if this is desired
def a2 = if (a == null) c else a;
Вот и не ясно, толи это ограничения матча, толи это баг. Я написал Москалю, посмотрим, что об этом скажет. Так вот если это ограничение матча и фиксать они его не планируют, то мой макрос упростить никак нельзя, даже наоборот, он еще больше может усложниться.
VD>Зато могу дать предложение. Хорошо бы чтобы этот оператор поддерживал бы и option[T].
Это следующее, что я хотел сделать, как только он мало-мальски заработает
Если в кратце, то основная моя загвоздка описывается вот таким вот кодом:
макрос
macro @??? (exprA, exprB)
{
def typer = Macros.ImplicitCTX ();
def teB = typer.TypeExpr(exprB);
def tyA = typer.TypeExpr(exprA).Type;
def tyB = typer.TypeExpr(exprB).Type;
def tx = (tyA.Hint, tyB.Hint);
def upCast =
(texpr, ty) =>
TExpr.TypeConversion (texpr.loc, ty, texpr, ty,
ConversionKind.UpCast());
match (tx)
{
| (Some(Class(_, _)), Some(Class(_, _))) =>
def st = <[
if ($exprA != null)
$exprA
else
$(upCast(teB, tyA) : typed)
]>;
WriteLine($"Generated expression: $st");
st
| _ =>
Message.FatalError ($"Operator `???' cannot be applied to "
"operands of type `$tyA' and `$tyB'");
}
}
тестовый примерclass C1
public override ToString() : string"C1"class C2
public static @:(_ : C2) : C1
WriteLine("Implicit!")
C1()
public override ToString() : string"C2"
def c1 = null : C1
def c2 = C2()
def r = c1 ??? c2
WriteLine($"r: $r")
А непонимаю я собственно вот что: при компиляции тестового примера получаю ожидаемое сообщение:
Если вместо c1 ??? c2, тупо написать этот самый сгенерированный if:
def r = if (c1 != null) c1; else (c2 : C1)
WriteLine($"r: $r")
то при выполнении получу то, что хочу:
Implicit!
r: C1
а если оставить ???, то при выполнении:
r: C2
Почему, вроде бы правильно сгенерированный if выполняется не правильно, я не понимаю. Причем, если глянуть рефлектором, то код для обоих случаев получается идентичный:
Почему такие различия я ума не приложу.
Как можно посмотреть сгенеренный после выполнения макроса код? Т.е. финальный код, который будет скармливаться компилятору. В интеграции я видел, вы сделали такое раскрытие кода, но пока нет возможности скачать VSSDK, так что поглядеть в студии не могу.
Здравствуйте, FR, Вы писали:
FR>Правильней было бы все что нужно для прикидывания C# выделить в отдельную сущность (скажем библиотеку макросов — враперов) а не портить язык.
В конечном счете, было бы правильно, если б к этому все пришло, но выносить все эти вещи, поддерживаемые рантаймом, в отдельную сущность достаточно большой объем работы. Да и вообще, с приводимыми тут похожестями:
PI>>коллекции .net — коллекции nemerle PI>>nullable — option
не согласен:
— коллекции nemerle нисколько не пытаются заменить коллекции .net, а расширяют их
— nullable — это средство представления null-based value-типов, причем без пенальти, как с точки зрения использования памяти, так и с точки зрения быстродействия. option же имеет другое предназначение.
согласен только с этой:
PI>>delegates — function values
Здравствуйте, PhantomIvan, Вы писали:
PI>>впрочем, мне все равно, что использовать из этих двух вещей — ведь по сути что там, что здесь это вариант по сути
PI>все равно с точки зрения производительности
Вот как раз с точки зрения производительности, иногда и слудет отказаться от option в пользу nullable типов.
PI>а иногда структуры становятся классами, и наоборот (рефакторинг типа)
Первый раз слышу такое определение боксинга/анбоксинга
PI>и тогда, кажется, option более удобен, т.к. позволяет пустить всех под 1 гребенку
Да он ИМХО почти всегда удобней, но всегда есть "но"
Здравствуйте, FR, Вы писали:
FR>Правильней было бы все что нужно для прикидывания C# выделить в отдельную сущность (скажем библиотеку макросов — враперов) а не портить язык.
И как можно, например, поддержку делегатов выделить в библиотеку?
А оператор ?? и так будет в библиотеке которую в общем-то можно не использовать.
Вот только это стандартная библиотека и без нее никто не будет воспринимать язык.
Nemerle вообще два. Один базовый. А другой полный, в библиотеке.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ie, Вы писали:
ie>Что до пользования ими, то мне реально пригодились наллаблы лишь несколько раз, примерно в таких случаях:
ie>
ie> // с наллаблами:
ie> public int? GetValueN()
ie> {
ie> return надоВернутьЗначение ? 10 : (int?)null;
ie> }
ie> int? zn = GetValueN();
ie> if (zn != null)
ie> // do something
ie>
ie>С наллаблами код становиться лаконичнее, хотя и без них обойтись можно.
Тебе повезло, что использовался конкретный тип — int. А вот мне нужно было сделать тоже самое, но для абстрактного T. И я обломался, так как при этмо я обязан был пометить T как вэлью-тип (struct), а для бибилотеки это неприемлемо.
VD>>Что до оператора ??, то больно сложно у тебя что-то выходит. Мне кажется ты перемудрил. Ничего осмысленного о реализации сказать не могу (не вникал).
ie>Дык, я тоже думал проще получится, а оказалось сам оператор достаточно служный и имеет ряд ньюансов. На самом деле все эти ньюансы касаются наллабл-типов. С референс-типами все сводится к банальному ?:,
Очень станно. У наллабл есть свойство HasValue которое сводит работу с ним к тому же ?:. Но только ?: в Nemerle нет, так что к if()/else.
ie>но и тут у Немерла проблемы:
...
Это скорее всего баг в компиляторе. У них вообще с неявнями приведениями типов проблемы. Хорошо что это большая редкость.
ie>Вот и не ясно, толи это ограничения матча, толи это баг. Я написал Москалю, посмотрим, что об этом скажет.
Разумно. Потом странслуруй куда нить. А вообще надо было писать в конфу.
ie> Так вот если это ограничение матча и фиксать они его не планируют, то мой макрос упростить никак нельзя, даже наоборот, он еще больше может усложниться.
Приведи чистый код демонструрующий это ограничени (без нулаблов, и компилируемый).
ie>Если в кратце, то основная моя загвоздка описывается вот таким вот кодом:
... ie>А непонимаю я собственно вот что: при компиляции тестового примера получаю ожидаемое сообщение:
...
Незнаю. Вникать нет времени. Но тоже вижу что код явно какой-то слишком избыточный. В ральных условиях обработка option[] сводится к (псевдокод):
using System.Console;
module P
{
public GetValueWithDefault[T](value : option[T], default : T) : T
{
match (value)
{
| Some(x) => x
| None => default
}
}
}
def a = Some(1) : option[int];
def b = None() : option[int];
WriteLine(P.GetValueWithDefault(a, 0));
WriteLine(P.GetValueWithDefault(b, 0));
почему е тебя такие сложности получаются я не знаю. Видимо есть пробелмы, но их нужно решать, а не обходить или замазывать. Если компилятор глючит, то надо его править, а не наворачивать макрос. Твоей макрос долже быть не сильно сложее этого метода.
С nullable все должно быть точно так же:
using System.Console;
module P
{
public GetValueWithDefault[T](value : T?, default : T) : T
where T: struct
{
if (value.HasValue) value.Value else default;
}
}
def a = 1 : int?;
def b = null : int?;
WriteLine(P.GetValueWithDefault(a, 0));
WriteLine(P.GetValueWithDefault(b, 0));
Если этот код не компилируется (а это так, проверил), то нужно лать баг-репорт и ждать пока Камил или Москаль разберутся. Или самому поправить ошибки.
Аналогичный код на C# без проблем компилируется:
using System;
class P
{
static T GetValueWithDefault<T>(T? value, T def)
where T: struct
{
return value.HasValue ? value.Value : def;
}
static void Main(string[] args)
{
int? a = 1;
int? b = null;
Console.WriteLine(GetValueWithDefault(a, 0));
Console.WriteLine(GetValueWithDefault(b, 0));
}
}
В общем, не забивай голову и не трать зря время. Такие горы кода по такому примитивному случаю делать нельзя. Надо исправлять причины проблем, а не замазвать их проявления.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Ну что ж. В итоге после копания в компиляторе, разговоров с Москалем и крепких размышлений, пришел вот к чему:
Для написания оператора '??' одним 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?
Здравствуйте, ie, Вы писали:
IT>>Конечно, лучше выдавать вменяемые ошибки. ie>+100, а то эти невменяемые отмазки компилятора уже напрягают.
Ну так займись Там поле не паханное. Всё равно с такой диагностикой ошибок как сейчас в релиз пускать компилятор рановато.
IT>>А ещё лучше, сделать самому приведение, если это возможно. ie>Хмм. Какими правилами руководствоваться? Implicit и UpCast?
Я бы начал с рассмотрения частных случаев. Преобразование 5 к int? логично? Значит надо преобразовывать. И т.д.
Если нам не помогут, то мы тоже никого не пощадим.
ie>>int? p = null;
ie>>int? q = null;
ie>>if (p ?? q == 0); // тут мне 0 нафиг не нужен
ie>>
VD>Я незнаю что тебе "тут" не нужно. Но такой оператор не нужен никому. В нем просто нет смысла.
Смысл можно найти во всем, но то что такой случай редкость, спорить не буду.
VD>А вот где в спецификации C# такое написано мне было бы интересно поглядеть. Тыкни, плиз пальцем. Или процитируй.
The type of the expression a ?? b depends on which implicit conversions are available between the types
of the operands. In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a, B is the
type of b, and A0 is the type that results from removing the trailing ? modifier, if any, from A. Specifically,
a ?? b is processed as follows:
• If A is not a nullable type or a reference type, a compile-time error occurs. • If A is a nullable type and an implicit conversion exists from b to A0, the result type is A0. At run-time, a
is first evaluated. If a is not null, a is unwrapped to type A0, and this becomes the result. Otherwise, b is
evaluated and converted to type A0, and this becomes the result.
• Otherwise, if an implicit conversion exists from b to A, the result type is A. At run-time, a is first
evaluated. If a is not null, a becomes the result. Otherwise, b is evaluated and converted to type A, and
this becomes the result.
• Otherwise, if an implicit conversion exists from A0 to B, the result type is B. At run-time, a is first
evaluated. If a is not null, a is unwrapped to type A0 (unless A and A0 are the same type) and converted
to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.
Otherwise, a and b are incompatible, and a compile-time error occurs.
Т.е. если оператор применяется к 2-м nullable, то результатом всегда будет nullable.
VD>А пока вот простенький тестик на C# 2.0: VD>
Дык, тут как раз попадаем под 2-ой пункт, где B приводимо к A0, т.к. A == int?, A0 == int, B == int.
VD>Что-то мне не веритися, что в МС реализовали эту фичу не по спецификации.
Здравствуйте, VladD2, Вы писали:
IT>>Нормальные задачи, например, отложенная инициализация свойст недефолтными значениями.
VD>Это у тебя что свойство будет на ходу тип менять?
Это как У нас же статически типизированный язык. Недефолтное значит, что string может равняться не string.Empty, а "something else".
VD>Единственное применение это: VD>
VD>def x : int? = null;
VD>def x ::= 2;
VD>
VD>что по-моему совершенно не очевидно и даже нелепо. В общем, я против такого.
Против такого я тоже против
Если нам не помогут, то мы тоже никого не пощадим.
IT>Или вот, например, NemerleProjectNode, line 137-145:
IT>
IT>protected internal VSLangProj.VSProject VSProject
IT>{
IT> get { _vsProject ??= new OAVSProject(this); }
IT>}
IT>
IT>По-моему, более чем понятно что здесь происходит.
Мне это решительно не нравится. Такое "сокращение" резко усложнит понимание. Оно не интуитивно. И практически ничего не дает. Так как таких пест впрограмме не много. Если тебе уж хочется автоматизировать подобные вещи, то лучше создай просто фукнцию типа:
AutoInit[T](var : ref T, creator : void) : T
where T: class
{
when (var == null)
var = creator();
var;
}
тогда хотя бы будет понятно что присходит и не будет лишней сущьности.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
IT>А ?? не путает? Боюсь, что не путает только потому, что это уже есть в C#.
?? интуитивно понятен и не создает странного побочного эффекта. В общем, я все болше и больше против такого расширения. Это идея от избытка возможностей. Совершенно лишняя, по-моему.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ie, Вы писали:
ie>Смысл можно найти во всем, но то что такой случай редкость, спорить не буду.
Ну, так поясни мне его. А то я его не вижу в упор. Если речь идет не о nullable-типах, а о ссылочных, то это другое дело. Ты все равно это как частный случай обрабатываешь.
ie>Да нет, сам проверял, все по спецификации.
С Some() у тебя точно ерунда выходит, так как из него точно никаких неявных приведений типов нет. Так что это по любому ошибка.
В отношении nullable-типов что-то я пока торможу. Может ты и прав. Надо не спросони еще раз проанализировать.
Так что по крайней мере разберись с Some(x)/None().
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Мне это решительно не нравится. Такое "сокращение" резко усложнит понимание. Оно не интуитивно. И практически ничего не дает.
А '??' тебе нравится? Такое сокращение не резко усложняет понимание? Оно интуитивно? Практически что-то делает?
Боюсь, что у этого сокращения есть только один плюс — оно уже в C#, поэтому включение аналога в N не вызывает такого негодования. Стоило предложить, все покивали головами и тут же побежали его делать. '??=' при наличии '??' — это уже не так страшно, поверь мне. Если понимать, что такое первые два '??', то '=' проблем уже не вызывает. Впрочем, мне всё равно, но на реакцию, основанную исключительно на личных предпочтениях, посмотреть было забавно
VD>Так как таких пест впрограмме не много.
Ты их все сосчитал? Только в своём коде? Осталось только добавить, что это вообще плохой паттерн, назвать его бредом и заклеймить всех, кто его использует
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, VladD2, Вы писали:
IT>>А ?? не путает? Боюсь, что не путает только потому, что это уже есть в C#.
VD>?? интуитивно понятен
Нифига он не понятен. Ставил эксперименты над несколькими людьми. Ни один человек сходу интуитивно не понял. Ни один!
VD>и не создает странного побочного эффекта.
Как насчёт: '::=', '&=', '|=' и даже '='? Они тоже создают странные побочные эффекты?
VD>В общем, я все болше и больше против такого расширения. Это идея от избытка возможностей. Совершенно лишняя, по-моему.
Да ради бога, я и не настаиваю. Смешно только смотреть, как странные фичи от MS воспринимаются с открытым ртом, а такие же по странности, но другие отвергаются с негодованием. Если уж на то пошло, то хотя я и предложил добавить '??' в N, но шедевром этот оператор не считаю. А уж в N без короткого if — ?:, он вовсе нелогичен и ему там вообще делать нечего.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, VladD2, Вы писали:
ie>>Смысл можно найти во всем, но то что такой случай редкость, спорить не буду.
VD>Ну, так поясни мне его. А то я его не вижу в упор. Если речь идет не о nullable-типах, а о ссылочных, то это другое дело. Ты все равно это как частный случай обрабатываешь.
Например такой:
def GetValue(p) : int?
{
// возвращаем зависимое от p значение
}
...
...
foreach (i in $[1..20])
res = res ?? GetValue(i); // мож сделать оператор '=??' в догонку к '??' и '??=' ? :)))
Ситуация конечно надуманная, но я не исключаю возможности ее возникновения. Аналогично с option.
VD>С Some() у тебя точно ерунда выходит, так как из него точно никаких неявных приведений типов нет. Так что это по любому ошибка.
Дык, из nullable тоже нет никаких неявных приведений.
VD>Так что по крайней мере разберись с Some(x)/None().
Я бы разобрался, но тоже не могу понять как правильно то надо?
Не зависимо от того является ли второй операнд option, возвращать Value? — А что тогда делать если оба None?
Здравствуйте, IT, Вы писали:
IT>>>А ?? не путает? Боюсь, что не путает только потому, что это уже есть в C#. VD>>?? интуитивно понятен IT> IT>Нифига он не понятен. Ставил эксперименты над несколькими людьми. Ни один человек сходу интуитивно не понял. Ни один!
Какого плана экспиримент? Только что спросил джависта, то ли у них там в джаве что-то похожее есть, но со второй попытки угадал. (Первая была: "какой-то муд@к полез ставить && в русской раскладке" )
VD>>В общем, я все болше и больше против такого расширения. Это идея от избытка возможностей. Совершенно лишняя, по-моему. IT>Да ради бога, я и не настаиваю. Смешно только смотреть, как странные фичи от MS воспринимаются с открытым ртом, а такие же по странности, но другие отвергаются с негодованием. Если уж на то пошло, то хотя я и предложил добавить '??' в N, но шедевром этот оператор не считаю. А уж в N без короткого if — ?:, он вовсе нелогичен и ему там вообще делать нечего.
Здравствуйте, IT, Вы писали:
ie>>Эээ, а причем тут кототкий if? IT>При том, что ?? — это расширение оператора ?:, которого в N нет, т.е. пропущен один шаг эволюции
Здравствуйте, IT, Вы писали:
IT>Нифига он не понятен. Ставил эксперименты над несколькими людьми. Ни один человек сходу интуитивно не понял. Ни один!
А ты им сказал что он должен делать? Я вот прочтя строчку описания и поглядев пример сразу понял о чем идет речь.
VD>>и не создает странного побочного эффекта.
IT>Как насчёт: '::=', '&=', '|=' и даже '='? Они тоже создают странные побочные эффекты?
Отлично. Эти операторы принимают и возвращают значение одного и того же пипа, а ?? разного.
Так что как насчет :=, :>= и прочего бред?
IT>Да ради бога, я и не настаиваю.
Ну, и отлично, так как я категорически против этого извращения.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ie, Вы писали:
ie>Здравствуйте, VladD2, Вы писали:
ie>>>Смысл можно найти во всем, но то что такой случай редкость, спорить не буду.
VD>>Ну, так поясни мне его. А то я его не вижу в упор. Если речь идет не о nullable-типах, а о ссылочных, то это другое дело. Ты все равно это как частный случай обрабатываешь.
ie>Например такой:
ie>
ie>def GetValue(p) : int?
ie>{
ie> // возвращаем зависимое от p значение
ie>}
ie>...
ie>...
ie>foreach (i in $[1..20])
ie> res = res ?? GetValue(i); // мож сделать оператор '=??' в догонку к '??' и '??=' ? :)))
ie>
ie>Ситуация конечно надуманная, но я не исключаю возможности ее возникновения. Аналогично с option.
Ситуация не надоуманная, а бредовая. Да и преимер не определен. Что за тип у res?
Что касается оперовтов =??, ??=. Я гляжу у вас ребяты от избытка возможностей крушу рвет. Возмите себя в руки. Новшества не должны создавать проблем. Они должны решать имеющиеся. "Крутость" ни в коем случае не долнжна становиться аспектом выбора.
VD>>С Some() у тебя точно ерунда выходит, так как из него точно никаких неявных приведений типов нет. Так что это по любому ошибка.
ie>Дык, из nullable тоже нет никаких неявных приведений.
Как же нет если 'int?' по твоим же словам приводится к 'int' ?
VD>>Так что по крайней мере разберись с Some(x)/None().
ie>Я бы разобрался, но тоже не могу понять как правильно то надо? \
Я тебе уеж говорил. Забей на всесь булшит что ты начитался в стандарте и сделай тупо:
ie>Не зависимо от того является ли второй операнд option, возвращать Value? — А что тогда делать если оба None?
Просто рассчитывать на то что второй аргумент не можут быть option[]. Я даже представить не могу разумного использования такой ситуации. Если кому-то надо возвратить option[] пусть использует паттерн-матчинг.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
ie>>def GetValue(p) : int?
ie>>{
ie>> // возвращаем зависимое от p значение
ie>>}
ie>>...
ie>>...
ie>>foreach (i in $[1..20])
ie>> res = res ?? GetValue(i); // мож сделать оператор '=??' в догонку к '??' и '??=' ? :)))
ie>>
ie>>Ситуация конечно надуманная, но я не исключаю возможности ее возникновения. Аналогично с option. VD>Ситуация не надоуманная, а бредовая.
Спасибо, сам боялся себе признаться, что я брежу.
VD>Да и преимер не определен.
Надо выполнить некоторые действия для всех параметров, при этом результатом должно быть первое не null значение.
VD>Что за тип у res?
Разве не очевидно? 'int?'
VD>Что касается оперовтов =??, ??=. Я гляжу у вас ребяты от избытка возможностей крушу рвет. Возмите себя в руки. Новшества не должны создавать проблем. Они должны решать имеющиеся. "Крутость" ни в коем случае не долнжна становиться аспектом выбора.
Ты смайлик видел?
VD>>>С Some() у тебя точно ерунда выходит, так как из него точно никаких неявных приведений типов нет. Так что это по любому ошибка. ie>>Дык, из nullable тоже нет никаких неявных приведений. VD>Как же нет если 'int?' по твоим же словам приводится к 'int' ?
По каким словам? Неявно можно привести 'int' к 'int?', но не наоборот. Наоборот, только используя явное привидение.
VD>Я тебе уеж говорил. Забей на всесь булшит что ты начитался в стандарте и сделай тупо: VD>
Вот это точно ерунда. o1 ?? o2 — где тут $value1, а где тут $defValue? И вообще, куда ты дел o2?
ie>>Не зависимо от того является ли второй операнд option, возвращать Value? — А что тогда делать если оба None? VD>Просто рассчитывать на то что второй аргумент не можут быть option[]. Я даже представить не могу разумного использования такой ситуации. Если кому-то надо возвратить option[] пусть использует паттерн-матчинг.
Так из-за чего сыр-бор то???
Если ты второй аргумент не option, то работать будет так, как ты и хочешь, не используй с option в качестве второго аргумента и будет тебе счастье.
def x = Some (1);
def y = Some (2);
def z = x ?? y ?? 3; // у 'z' будет тип 'int'
Здравствуйте, VladD2, Вы писали:
IT>>Нифига он не понятен. Ставил эксперименты над несколькими людьми. Ни один человек сходу интуитивно не понял. Ни один!
VD>А ты им сказал что он должен делать?
Зачем? Всё же интуитивно понятно
VD>>>и не создает странного побочного эффекта. IT>>Как насчёт: '::=', '&=', '|=' и даже '='? Они тоже создают странные побочные эффекты? VD>Отлично. Эти операторы принимают и возвращают значение одного и того же пипа, а ?? разного.
Кто сказал разного? Я такого не говорил. Впрочем вот это:
int? n = 0;
n &= 1;
работает без проблем.
VD>Так что как насчет :=, :>= и прочего бред?
К чему это всё? Боюсь, ты опять что-то недопонял и решил додумать по своему.
IT>>Да ради бога, я и не настаиваю.
VD>Ну, и отлично, так как я категорически против этого извращения.
Почему же ты не против извращения '??' ?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, ie, Вы писали:
ie>Если ты второй аргумент не option, то работать будет так, как ты и хочешь, не используй с option в качестве второго аргумента и будет тебе счастье.
ie>
ie>def x = Some (1);
ie>def y = Some (2);
ie>def z = x ?? y ?? 3; // у 'z' будет тип 'int'
ie>
Здравствуйте, VladD2, Вы писали:
ie>>Если ты второй аргумент не option, то работать будет так, как ты и хочешь, не используй с option в качестве второго аргумента и будет тебе счастье.
ie>>
ie>>def x = Some (1);
ie>>def y = Some (2);
ie>>def z = x ?? y ?? 3; // у 'z' будет тип 'int'
ie>>
Здравствуйте, VladD2, Вы писали:
VD>Это не то что нужно. Нужно: VD>
VD>def a : option[int] = None();
VD>def b : int = a ?? 2;
VD>assert((a ?? 2).GetType().FullName == "System.Int32");
VD>assert(a ?? 2 == 2);
VD>def a : option[int] = Some(1);
VD>def b : int = a ?? 2;
VD>assert((a ?? 2).GetType().FullName == "System.Int32");
VD>assert(a ?? 2 == 1);
VD>
Ну да, это и так есть
Кстати, несколько дней назад я закомитил оператор в svn, можешь свои догадки относительно работы легко проверить.
VD>PS VD>И логики работы с option[] я все равно не понял.
Здравствуйте, ie, Вы писали:
ie>Ну да, это и так есть ie>Кстати, несколько дней назад я закомитил оператор в svn, можешь свои догадки относительно работы легко проверить.
ОК, так и сделаю. Но чуть позже. ie>Ну все же вроде просто, 2 правила:
ie>