Something[void]
От: artelk  
Дата: 01.12.11 16:22
Оценка:
А нельзя ли как-нибудь малой кровью добавить возможность параметризовать генерики типом void, раз уж даже такие вещи можно делать:
  mutable x : void = ();
  x = WriteLine("Hi");

?

Когда это может понадобится:
  //Выполнение кода в каком-то контексте
  ExecInContext[T](f : SomeContext -> T) : T
  {
    using(def ctx = SomeContext())
      f(ctx);
  }

Так вот, передать в ExecInContext функцию SomeContext->void нельзя.
Придется явно делать перегрузку:
  //Так, с копипастом
  ExecInContext(f : SomeContext -> void) : void
  {
    using(def ctx = SomeContext())
      f(ctx);
  }

  //Или как-нибудь так
  ExecInContext(f : SomeContext -> void) : void
  {
    _ = ExecInContext(ctx => { f(ctx); null : object; });
  }


Или, например, вместо void использовать везде FakeVoid из computation expressions.

Так вот, нельзя ли автоматизировать все это?
Например, добавить структуру
public struct VoidStruct
{
  public static Value : VoidStruct = VoidStruct();
}

И научить компилятор делать замены в коде.
Т.е., например:
ExecInContext(_ => {/*...*/});//если тело лямбды имеет тип void
ExecInContext.[VoidStruct](_ => {/*...*/; VoidStruct.Value; });//заменять так


Для случая генериков с указанием "where T:class" использовать класс VoidClass:
public abstract class VoidClass
{
  public static Value : VoidClass = null;
  private this() {}
}

ExecInContext_Class[T](f : SomeContext -> T) : T
      where T : class
{
  using(def ctx = SomeContext())
    f(ctx);
}

ExecInContext_Class(_ => {/*...*/});//если тело лямбды имеет тип void
ExecInContext_Class.[VoidClass](_ => {/*...*/; VoidClass.Value;});//заменять так


Есть проблема, если в качестве параметра f передается не лямбда, создаваемая по месту, а, например, некая уже существующая функция T->void:
module Test
{
  F(ctx : SomeContext) : void { /*...*/ }
}

ExecInContext(F);
//придется заменять на
ExecInContext(ctx => {F(ctx); VoidStruct.Value; });

Или:
def f : SomeContext->void = ctx => {/*...*/};
//тут какой-то код, для которого требуется, чтобы f была с типом SomeContext->void
ExecInContext(f);//тоже придется оборачивать


В этом случае создается новый объект и передается в качестве параметра.
В случае делегатов это может привести, например, к невозможности отписки от событий.

Нет ли возможности как-то решить эту проблему?

PS На правах размышлений вслух.
Re: Something[void]
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.12.11 16:41
Оценка:
Здравствуйте, artelk, Вы писали:

A>Так вот, нельзя ли автоматизировать все это?


В F# пошли этим путем и придумали тип unit аналогичный FakeVoid. Результатом стало ухудшение совместимости с дотнетными библиотеками (и так хреновая) и непроизводительные затраты, так как даже возвращение пустой структуры влияет на производительность вызова.

На мой взгляд, можно сделать следующее: FakeVoid можно ввести в состав стандартной библиотеки и, возможно, реализовать некие функции, макросы или операторы для упрощения генерации оберток.

Можно так же сделать закладку в компиляторе, чтобы приведение типов от X -> void к X -> Y автоматически вставляла лямбду типа X -> FakeVoid.

А идея вот встраивание этого дела в язык, без поддержки со стороны рантайма, мне хорошей не кажется.

Можно было бы попробовать для каждой функции генерировать перегрузку для void, но тогда это не будет работать со внешними библиотеками и может привести к генерации кучи ненужных дубликатов.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Something[void]
От: _nn_ www.nemerleweb.com
Дата: 01.12.11 17:17
Оценка: +1
Здравствуйте, artelk, Вы писали:

А нельзя ли сделать макру которая сама сгенерирует код ?
[AddVoidOverload]
ExecInContext[T](f : SomeContext -> T) : T
{
  using(def ctx = SomeContext())
    f(ctx);
}


==>

ExecInContext(f : SomeContext -> void) : void
{
  using(def ctx = SomeContext())
    f(ctx);
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Something[void]
От: catbert  
Дата: 01.12.11 20:25
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>На мой взгляд, можно сделать следующее: FakeVoid можно ввести в состав стандартной библиотеки и, возможно, реализовать некие функции, макросы или операторы для упрощения генерации оберток.


Только назвать его unit.
Re[3]: Something[void]
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.12.11 22:40
Оценка:
Здравствуйте, catbert, Вы писали:

VD>>На мой взгляд, можно сделать следующее: FakeVoid можно ввести в состав стандартной библиотеки и, возможно, реализовать некие функции, макросы или операторы для упрощения генерации оберток.


C>Только назвать его unit.


— Зачем?
— Тсссс! Чтобы никто не догадался!

(с) Операция "Ы".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Something[void]
От: Ziaw Россия  
Дата: 02.12.11 01:37
Оценка: 1 (1)
Здравствуйте, _nn_, Вы писали:

__>А нельзя ли сделать макру которая сама сгенерирует код ?


Вообще конечно можно, но это просто воркэраунд бага в реализации. Идеология подразумевает, что int -> int и int -> void вполне равноправные функции различающиеся только вторым типом.

И совершенное логично, что если первый тип можно использовать в дженериках, то и второй тоже должно быть можно. Так что если надо ехать сейчас, то конечно макра это выход. А вообще, надо что-то изобретать. Например, то, что предложил Влад.
Re[2]: Something[void]
От: artelk  
Дата: 02.12.11 06:37
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Здравствуйте, artelk, Вы писали:


__>А нельзя ли сделать макру которая сама сгенерирует код ?

__>
__>[AddVoidOverload]
__>ExecInContext[T](f : SomeContext -> T) : T
__>{
__>  using(def ctx = SomeContext())
__>    f(ctx);
__>}
__>


__>==>


__>
__>ExecInContext(f : SomeContext -> void) : void
__>{
__>  using(def ctx = SomeContext())
__>    f(ctx);
__>}
__>


Ага, это первое, что приходит в голову. Только такое дублирование делать не совсем "честно".
Например, если повесить этот макроатрибут на метод интерфейса, то возникнут 2 разных метода, каждый из которых придеться реализовать в наследниках отдельно.

Или вот: часто вместе с генерик классом делается негенерик класс с тем же именем, в который добавляются некие хэлперные статические методы или методы расширения и т.п. Т.е. навестить такой макроатрибут на генерик класс не получится — он сгенерирует негенерик класс с тем же именем и будет неоднозначность.

Генерики могут иметь более одного параметра:
[AddVoidOverload]
F[T1, T2]():void{}

Тут нужно сгенерировать 3 перегрузки:
1. Для F[T1, void] — с сигнатурой F[T1]()
2. Для F[void, T2] — с сигнатурой F[T2]()
3. Для F[void, void] — с сигнатурой F()
Итого, вместе с изначальным вариантом, 2^N перегрузок, где N — число параметров.
Проблема в том, что перегрузки 1. и 2. имеют одинаковую сигнатуру. И нужно добавлять какие-то неочевидные префиксы/постфиксы к названиям методов, чтоб разрешить эту неоднозначность...
Re[2]: Something[void]
От: artelk  
Дата: 02.12.11 09:24
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, artelk, Вы писали:


A>>Так вот, нельзя ли автоматизировать все это?


VD>В F# пошли этим путем и придумали тип unit аналогичный FakeVoid.

Скорее FakeVoid — аналог unit
Имхо, лучше его сделать пустым tuple-ом, тем более, что и задается он как "()".

VD>Результатом стало ухудшение совместимости с дотнетными библиотеками (и так хреновая) и непроизводительные затраты, так как даже возвращение пустой структуры влияет на производительность вызова.

То, что возврат пустой структуры это тоже затраты — просто ужас. Не ожидал такой подставы.

Я не предлагаю везде вместо void вставлять unit и на каждый чих вставлять обертки. Обертки будут только когда параметр генерика выводится или явно указывается как void. И причем только для лямбд, возвращающих этот void. И причем даже не всегда. Если лямбда задается по месту при вызове функции, в которую она передается, то оборачивать ее не нужно — достаточно в конец тела лямбды добавить "VoidStruct.Value;".

Дело в том, что, на сегодняшний день, в любом случае, чтобы не копипастить код, приходится писать что-то типа:
  ExecInContext(f : SomeContext -> void) : void
  {
    _ = ExecInContext(ctx => { f(ctx); null : object; });
  }

Тут обертка создается всегда.

Вот и хочется, чтобы язык, по возможности, скрывал этот маразм рантайма.

VD>На мой взгляд, можно сделать следующее: FakeVoid можно ввести в состав стандартной библиотеки и, возможно, реализовать некие функции, макросы или операторы для упрощения генерации оберток.

Тут слишком дофига частных случаев будет при генерации. См. http://rsdn.ru/forum/nemerle/4522085.1.aspx
Автор: artelk
Дата: 02.12.11


VD>Можно так же сделать закладку в компиляторе, чтобы приведение типов от X -> void к X -> Y автоматически вставляла лямбду типа X -> FakeVoid.

Ага

VD>А идея вот встраивание этого дела в язык, без поддержки со стороны рантайма, мне хорошей не кажется.

Почти все полезное в Nemerle (чего нет в C#) делается без поддержки рантайма: варианты, неизменяемые переменные, лямбда-функции (спасибо, что не через делегаты)...

VD>Можно было бы попробовать для каждой функции генерировать перегрузку для void, но тогда это не будет работать со внешними библиотеками и может привести к генерации кучи ненужных дубликатов.

Так и есть.
Re: Something[void]
От: artelk  
Дата: 02.12.11 11:24
Оценка:
Просьба к тем, у кого настроена среда.
Пожалуйста, для проверки концепции, добавьте в Nemerle.Builtins.FunctionVoid['p1] следующий метод:

  public static @: (f : FunctionVoid['p1]) : 'p1 -> System.Reflection.Missing
  {
      p1 => {f.apply_void(p1); System.Reflection.Missing.Value;}
  }


И, после этого, просьба потестить следующий код:

  Exec[T](f : object -> T) : T
  {
      f(null);
  }

  _ = Exec(_=>{});


PS Или не все так просто? FunctionVoid['p1], например, наследуется от Function['p1, object], только _=>{} почему-то не приводится к T->object...
Re[3]: Something[void]
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.12.11 15:09
Оценка: +2
Здравствуйте, Ziaw, Вы писали:

Z>Вообще конечно можно, но это просто воркэраунд бага в реализации. Идеология подразумевает, что int -> int и int -> void вполне равноправные функции различающиеся только вторым типом.


Ну, да. Бага в дизайне дотнета.

Надо поискать в коннекте нет ли подобных предложений, если есть поддержать, если нет завести новое. Поддержка void-а в дженериках была бы полезна всем языкам дотнета. Даже шарпу.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Something[void]
От: _NN_ www.nemerleweb.com
Дата: 01.10.13 07:42
Оценка: 1 (1) +1
Здравствуйте, artelk, Вы писали:

A>А нельзя ли как-нибудь малой кровью добавить возможность параметризовать генерики типом void, раз уж даже такие вещи можно делать:


Предлагаю внести класс Unit в стандартную поставку Nemerle и разрешить void в обобщенных типах.
Кто за ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Something[void]
От: hardcase Пират http://nemerle.org
Дата: 04.10.13 13:04
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Кто за ?


Я против. Радость отладки разруливания перегрузок предоставлю вкусить тебе.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[4]: Something[void]
От: STDray http://stdray.livejournal.com
Дата: 04.10.13 13:40
Оценка:
VD>Надо поискать в коннекте нет ли подобных предложений, если есть поддержать, если нет завести новое. Поддержка void-а в дженериках была бы полезна всем языкам дотнета. Даже шарпу.

Было. Неожиданные люди интересовались
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.