Здравствуйте, _Claus_, Вы писали:
_C_>собственно интуитивно ожидается, что должен работать и возвращать Value, однако же нет. _C_>недоработка или что-то другое?
?. защищает доступ к членам. Откровенно говоря я не предполагал, что кто-то будет использовать его с option[T] для обхода None.
С другой стороны ?? родственный оператор и он работает с option[T]. Так что даже не знаю.
К тому же option[T] — это сам по себе ссылочный тип и члены возвращающие этот тип могут содержать null. Так что если изменить его поведение, то могут быть проблемы в других местах.
В общем, хотелось бы услышать другие мнения. Нужно ли прикручивать поддержку option[T] к "?.", и что при этом делать с проверкой на null самого члена типа option[T]?
ЗЫ
Чем больше думаю над option[T], тем больше понимаю, что надо было его структурой делать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, _Claus_, Вы писали:
_C_>собственно интуитивно ожидается, что должен работать и возвращать Value, однако же нет. _C_>недоработка или что-то другое?
Да. Забыл добавить. Ты можешь использовать метод WithDefault. Он делает ровно что тебе нужно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>В общем, хотелось бы услышать другие мнения. Нужно ли прикручивать поддержку option[T] к "?.", и что при этом делать с проверкой на null самого члена типа option[T]?
VD>ЗЫ
VD>Чем больше думаю над option[T], тем больше понимаю, что надо было его структурой делать.
хотел вот именно это все сразу написать, но потом подумал, что это как бы очевидно и так, и не стоит.
но ессно option[T] должна быть структурой по реализации, даже если ее удобно показывать как вариант.
иначе накладные расходы и странности в использовании.
соответственно и ?. должен вести себя предсказуемо.
Здравствуйте, catbert, Вы писали:
C>Здравствуйте, hardcase, Вы писали:
H>>Это несовсем верно. У ?. ленивая семантика, тогда как у WithDefault — энергичная.
C>Это как? Если я передам a?.b в метод, оно не будет вычисляться пока аргумент не понадобится?
Речь идет не о передачи значения всего выражения, но о вычислении правого аргумента. Праый аргумент операторов ?. и ?? вычисляется лишь при условии.
Здравствуйте, _Claus_, Вы писали:
_C_>но ессно option[T] должна быть структурой по реализации, даже если ее удобно показывать как вариант.
По моему надо с любым вариантом сделать что то подобное -- запретить любым вариантам принимать значение null.
В хаскеле, например, Алгебраические типы (читай варианты) по поределению не могут быть null, собственно и понятия такого как null там нет.
Но у нас есть пережиток .NET'a как NULL.
Ну вообще исключать null из языка -- гемор в использовании, но в случаи с вариантами null не нужен. Например как list может быть null? по логике он должен быть либо [], либо [a1..an]. То же можно сказать про любой другой вариант, там где нужно неопределенное значение можно использовать option.
Поясню по другому: где видим класса -- понимаем что это может быть, либо null, либо экземпляр. В случае с вариантами -- подразумеваем, что это один из вариантов(извините за тавтологию)
Здравствуйте, BogdanMart, Вы писали:
BM>Ну вообще исключать null из языка -- гемор в использовании
Если оборачивать все либы в опции, то гемор, но внешние либы не гарантируют что поля не будут null, даже если это означает ошибку либы. Но лепить визде опшины тоже не дело.
То есть в кошерном коде null не будет по определению(либо варианты, либо свой класс с объявленным контрактом). Ну а там где иидет взаимодейтсвие сс внешним кодом могут быть нулы.
То есть объявить святотатство класса notnull
BM>По моему надо с любым вариантом сделать что то подобное -- запретить любым вариантам принимать значение null.
Наделе можно так сделать:
[NotNull]
class A{};
class B:A{};
class C
{
this()
{
a = A();
} // Error: nonnul immutable field a' must be initializedpublic a:A;
public a':A;
public mutable a'':A; // Error: public fieelds can't expose non-null typesprivate mutable a''':A = A(); // All ok, check for non-null contract will be emited by compiler on use casespublic mutable a'''':A = A(); // Error: public fieelds can't expose non-null typesinternal setA(A aa):void
{
a''' = aa; // поскольку метод internal , то мы уверены что здесь нету нулл'а
}
public setA2(A aa):void// поскольку метод public , то если аа == Null -- бросаем ArgumentException
{
a''' = aa;
}
}
это создаст следующий код(то что не скомпилится выбросил):
[NotNull]
class A{};
[NotNull]
class B:A{};
class C
{
this()
{
a = A();
}
public a:A;
private mutable a''':A = A();
internal setA(A aa):void
{
a''' = aa;
}
internal setA_Internal_N_35483(A aa):void
{
a''' = aa;
}
public setA2(A aa):void
{
if (aa == null)
throw ArgumentException(...)
else
setA_Internal_N_35483(aa)
}
}
// ну и напоследок сладость для доступа к такому методу из другой сборки написанной на немерлеpublic NonNullInteropHelper_Ϡ
{
public static C__setA_Internal_Ϡ_35483(C c, A aa):void// иероглиф, чтобы ненароком с C# не вызвали
{
c.setA_Internal_N_35483(aa)
}
}
можно вообще какой то непечатаемый символ ( обфускатор использует символ конца строки в имени метода,так как CLR просто транслирует Unicode, то ему по фиг, а ни один компилер такое не распарсит
Вопрос целесообразно ли делать такой трюк, чтобы обойти проверку на Null при вызове из другой либы на Nemerle? думаю не очень, не такие большие накладные расходы.
Но если прикрутить нативную поддержку компилятором и других контрактов, то тогда есть смысл.
Здравствуйте, VladD2, Вы писали: VD>В дотнете это физически невозможно. Например, null-ссылка может быть получена внутри массива или внутри структуры созданной дефолтным конструктором.
Да с массивом и структурой действительно лажа..
Но подобную штуку видел в С++/CIL
struct A
{
int i;
String^ s; //Error can't use managed pointer inside unmanged structchar Cs[20];
}
struct val B
{
int i;
String^ s; //All okchar Cs[20]; //Critical warning -- mixed types not allowed
// светится как ошибка, но прагмой специальной можно отключить, но тогда эта струкутра не будет CLS Compilant
}
Так что для таких типов можно запретить массивы/структы.
Ты часто видел массивы варантов? а не листы, например.
можно:
вставлять проверки на Null и исключение по несоотвтесвию контарктов в точах соприкосовения NonNull типов и внешних дотнетовских тпов
запретить использовать наши типы с внешними, небезопасными
какой то свой, "безоасный" тип структа, как в С++/CLI
Здравствуйте, _Claus_, Вы писали:
_C_>но ессно option[T] должна быть структурой по реализации, даже если ее удобно показывать как вариант.
Можно ввести понятие варианта "по умолчанию" для вариантных типов, это может быть любой не гиперболизированный вариант указанный разработчиком.
Например для option это None а для list[T] это Nil итд.
И когда идет матч по варианту, если видим null обрабытваем как вариант по умолчанию.
(подводный камень -- если есть какие то методы)
Здравствуйте, _Claus_, Вы писали:
_C_>но ессно option[T] должна быть структурой по реализации, даже если ее удобно показывать как вариант. _C_>иначе накладные расходы и странности в использовании.
Заинтересовался вопросом накладных расходов на класс по сравнению со структурой ещё из прошлого обсуждения в какой-то другой теме.
В некоторых модулях в рабочем проекте (правда на C#) активно используется самодельный Option вида:
public abstract class Option<T>: IEnumerable<T> {
public abstract bool HasValue { get; }
public abstract T Value { get; }
}
public sealed class Some<T>: Option<T> {
...
}
public sealed class None<T>: Option<T> {
...
}
Из любопытства проверил следующие варианты:
1. Option в реализации, приведённой выше
2. Option без наследования (то есть пустое поле под Value будет даже у None)
3. Option в виде структуры
4. Алгоритм вообще без Option
Дело было недели 3 назад, поэтому конкретных цифр, к сожалению, не осталось.
В целом, первые три варианта отличались совсем незначительно и по большей части разница была только в объёме используемой памяти и количестве сборок мусора.
Можно даже сказать, что вариант со структурами был несколько медленнее с точки зрения затраченного времени, но эффективнее в плане нагрузки на память и сборщик.
Вариант без Option'ов работал где-то процентов на 30-40 быстрее, чем с оными, но код при этом стал раза в 3 страшнее.
Здравствуйте, Uriel, Вы писали:
U>Дело было недели 3 назад, поэтому конкретных цифр, к сожалению, не осталось. U>В целом, первые три варианта отличались совсем незначительно и по большей части разница была только в объёме используемой памяти и количестве сборок мусора. U>Можно даже сказать, что вариант со структурами был несколько медленнее с точки зрения затраченного времени, но эффективнее в плане нагрузки на память и сборщик. U>Вариант без Option'ов работал где-то процентов на 30-40 быстрее, чем с оными, но код при этом стал раза в 3 страшнее.
Talk is cheap
Было бы интересно посмотеть на бенчмарки и реализацию структуры. Особенно меня смущает реализация IEnumerable<T>.
Здравствуйте, hardcase, Вы писали:
H>Talk is cheap H>Было бы интересно посмотеть на бенчмарки и реализацию структуры. Особенно меня смущает реализация IEnumerable<T>.
Да я понимаю, просто привычка поганая есть удалять всё после того, как оно уже не актуально.
Вечером гляну, может быть остались исходники, с которыми я баловался.
А IEnumerable<T> навеяно реализацией Option'ов в Scala, где их можно пользовать в конструкции for.
Ну и к тому же очень удобно иногда впихивать операции над Option'ами в портянку вызовов LINQ, a-la:
public Option<T> Foo(Bar input);
IEnumerable<T> foo = SomeCollection.SelectMany(_ => Foo(_));
Здравствуйте, Uriel, Вы писали:
U>А IEnumerable<T> навеяно реализацией Option'ов в Scala, где их можно пользовать в конструкции for. U>Ну и к тому же очень удобно иногда впихивать операции над Option'ами в портянку вызовов LINQ, a-la: U>
Не лучше ли такой метод использовать:
IEnumerable<TResult> Choose<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Option<TResult>> selector) { return source.Select(selector).Where(Option.IsSame); }
?
Ну и вообще в качестве примера http://msdn.microsoft.com/en-us/library/ee370544.aspx
Здравствуйте, Uriel, Вы писали:
U>В целом, первые три варианта отличались совсем незначительно и по большей части разница была только в объёме используемой памяти и количестве сборок мусора.
Конечно, сперва нужно смотреть на код. Так ничего сказать нельзя.
Но "отличались совсем незначительно" скорее всего является следствием синтетичности тесто. По видимому в этих тестах объем работы с option был незначителен по сравнению с основном объемом работы.
Но бывают и другие ситуации. Например, в компиляторе немерла option используется очень плотно. При этом каждый раз создаются новые экзепляры. Уверен, что замена его на структуру заметно повысит производительность.
U>Можно даже сказать, что вариант со структурами был несколько медленнее с точки зрения затраченного времени, но эффективнее в плане нагрузки на память и сборщик.
Это зависит от особенностей применения. Если будет создаваться большое количество экземпляров option-а, то структура должна быть значительно шустрее. Плюс на скорость влияет и способ ее возврата. К сожалению CLR генерирует медленный код когда из метода возвращается структура размер которой больше размера указателя.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, hardcase, Вы писали:
VD>>Да. Забыл добавить. Ты можешь использовать метод WithDefault. Он делает ровно что тебе нужно.
H>Это несовсем верно. У ?. ленивая семантика, тогда как у WithDefault — энергичная.
"ленивая" тут не совсем точный термин. Но я понял твою мысль. WithDefault больше похож на ??. Это да.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.