Re[8]: Тип 'void'
От: _NN_  
Дата: 26.05.14 08:44
Оценка:
Здравствуйте, NeoCode, Вы писали:

NC>В данном случае синтаксически явно (скобками) выделен аргумент, внутри которого может быть что угодно. В языках, в которых блоки кода могут возвращать значения, можно воткнуть в аргмент целый кусок кода с циклами, условыми переходами и вероятно даже объявлениями классов. Против этого я ничего не имею.

NC>В случае void становятся возможными вставки void просто в основной список аргументов, т.е. со стороны это выглядит именно как передача полноценных аргументов
NC>
f( g(), 1 );
NC>f( g(), g(), 1, g(), g() );
NC>// и т.д.
NC>

NC>Более того, такое невозможно сделать ни с одним другим типом. ИМХО, это вносит путаницу и именно это мне не нравится.

Ну сейчас невозможно вызвать функцию с передачей void значение. Такое невозможно сделать с другим типом
В принципе такое нужно только в обобщенном коде.
Возможно в обычном действительно вносит лишнюю путаницу.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[9]: Тип 'void'
От: NeoCode  
Дата: 26.05.14 09:22
Оценка: +1
Здравствуйте, _NN_, Вы писали:

_NN>Ну сейчас невозможно вызвать функцию с передачей void значение. Такое невозможно сделать с другим типом

_NN>В принципе такое нужно только в обобщенном коде.
_NN>Возможно в обычном действительно вносит лишнюю путаницу.

Между обобщенным и обычным кодом не должно быть разницы, иначе это вообще полная фигня получится.
Re[7]: Тип 'void'
От: NeoCode  
Дата: 26.05.14 09:33
Оценка:
Здравствуйте, Sinclair, Вы писали:

NC>>По идее, в оригинальном виде тоже можно (полноценный тип же). Но из этого следует возможность расширить еще еще дальше, т.е. втыкать void в любые функции? Или не следует?

S>Нет, не следует. Попытки скормить default(void) в неожиданные места должны приводить к ошибкам типизации.

То есть получается следующее:
1. если функция объявлена (тем или иным способом — явно или в результате раскрытия обобщенного кода) например как foo(void,int,void,void), то ее можно "сокращать" ко всем вариантам, получаемым путем выкидывания одного или нескольких void. Все эти варианты будут эквивалентны. Но только сокращать — вставлять void-значения в произвольные места в списке аргументов функции (туда где их не было в прототипе) нельзя.

Сразу вопрос — а должны ли быть эквивалентны варианты foo(void,int) и foo(int)? ведь void у нас теперь полноценный тип.

2. Функция, объявленная как foo(), эквивалентна функции foo(void), т.е. отсутствие аргументов вообще эквивалентно одному аргументу void. Это исключение вводится для того, чтобы можно было писать foo(bar()); для void.

А кому-то захочется передать foo(bar(), bar()); Ведь чем один void-аргумент лучше двух или трех?

Опять некрасивое противоречие, приводящее в конечном итоге все к тому же — к возможности пихать void-значнения в произвольные места списков аргументов функций при их вызове.
Re[8]: Тип 'void'
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 26.05.14 09:47
Оценка:
Здравствуйте, NeoCode, Вы писали:

NC>То есть получается следующее:

NC>1. если функция объявлена (тем или иным способом — явно или в результате раскрытия обобщенного кода) например как foo(void,int,void,void), то ее можно "сокращать" ко всем вариантам, получаемым путем выкидывания одного или нескольких void. Все эти варианты будут эквивалентны. Но только сокращать — вставлять void-значения в произвольные места в списке аргументов функции (туда где их не было в прототипе) нельзя.

NC>Сразу вопрос — а должны ли быть эквивалентны варианты foo(void,int) и foo(int)? ведь void у нас теперь полноценный тип.


Похожая логика уже есть для параметров со значениями по умолчанию: выкидывать можно, вставлять лишние нет; foo(int a, bool b = false) не эквивалентен foo(int a); даны правила для разрешения потенциальных неоднозначностей.
Ce n'est que pour vous dire ce que je vous dis.
Re[9]: Тип 'void'
От: NeoCode  
Дата: 26.05.14 11:08
Оценка:
Здравствуйте, Don Reba, Вы писали:

DR>Похожая логика уже есть для параметров со значениями по умолчанию: выкидывать можно, вставлять лишние нет; foo(int a, bool b = false) не эквивалентен foo(int a); даны правила для разрешения потенциальных неоднозначностей.


Соглсен. А с
void v();
void foo();
foo(v(), v(), v())

что делать?
Re: Тип 'void'
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.05.14 12:35
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Насколько логично относиться к 'void' или 'Unit' как к обычному типу ?

_NN>Скажем, логично ли требовать чтобы код с обычными типами работал и с void ?

Разумеется, логично.

_NN>А с void ?

_NN>
_NN>function f(a : void) {..}
_NN>function g(b : void) {..}
_NN>function h(c : void, d : void) {..}

_NN>var x = f();
_NN>f(g()); // OK ?
_NN>f(x); // OK ?
_NN>h(f(), g()); // OK ?
_NN>


если void это нормальный тип, то нет никакой проблемы — параметр тоже может быть void

в обычном коде это не нужно, а вот в дженериках очень даже нужно
Re[10]: Тип 'void'
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 26.05.14 15:24
Оценка:
Здравствуйте, NeoCode, Вы писали:

NC>Соглсен. А с

NC>
NC>void v();
NC>void foo();
NC>foo(v(), v(), v())

NC>что делать?

Если разрешить опускать void, но не вставлять лишние получается где-то так:

void v();
void foo0(); void foo1(void); void fooT<T>(T);
foo0(v());    // запретить!
foo1(v());    // OK
foo1();       // OK
fooT(v());    // OK
fooT<void>(); // OK


А ещё, мне не нравится потенциальная возможность параметризировать войдом при опущенном параметре:

fooT();       // запретить!
Ce n'est que pour vous dire ce que je vous dis.
Re[2]: Тип 'void'
От: Кодт Россия  
Дата: 26.05.14 16:32
Оценка:
Здравствуйте, deniok, Вы писали:

D>В языках с алгебраическими типами данных Unit — обычный тип, в котором имеется ровно одно значение.


На си то же самое можно сделать
struct SVoid {};


Тут, скорее проблема "безэлементный кортеж — это что?" и "одноэлементный кортеж — это что?"
В питоне, например, любой кортеж является последовательностью, и чтобы отличить кортеж от элемента, изобретён специальный синтаксис конструирования
n  = None # аналог void / unit
x  = 1
z  = 0

# кортежи
ns = ()
xs = (1,)
zs = (0,)
ys = (0,0) # сова? сова бьян!

print bool(n) # false - пустой тип
print bool(x) # true - ненулевое значение
print bool(z) # false - нулевое значение

print bool(ns) # false - пустой кортеж
print bool(xs) # true - непустой кортеж
print bool(zs) # true - непустой кортеж, хотя и из нулей
Перекуём баги на фичи!
Re[8]: Тип 'void'
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.05.14 17:26
Оценка:
Здравствуйте, NeoCode, Вы писали:

NC>2. Функция, объявленная как foo(), эквивалентна функции foo(void), т.е. отсутствие аргументов вообще эквивалентно одному аргументу void.

Я думаю, это лишнее.
NC> Это исключение вводится для того, чтобы можно было писать foo(bar()); для void.
Зачем? У вас есть функция foo(). Она ничего не принимает. Зачем вы хотите передать в неё результат функции, даже если она ничего не возвращает?
Если у нас есть foo<T>(T a), и T bar<T>(), то композиция функций — дело приемлемое, и она должна работать, в том числе, и для T=void. Но совершенно непонятно, зачем разрешать "передавать" в foo() результат bar<T>(), ведь такой код будет работать только для одного типа параметра. А для него вместо foo(bar()) можно написать bar();foo().
NC>А кому-то захочется передать foo(bar(), bar()); Ведь чем один void-аргумент лучше двух или трех?
Надо делать не то, что "кому-то захочется", а то, что упрощает типовые сценарии.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Тип 'void'
От: Кодт Россия  
Дата: 26.05.14 21:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Надо делать не то, что "кому-то захочется", а то, что упрощает типовые сценарии.


Я бы смягчил.

В конце концов, в С++ долго воевали за унификацию return smth и return void, в первую очередь, в шаблонах, — и к 2011 стандарту таки согласились, что можно, нужно и полезно.

То есть, сценарии есть, равно как и есть обходные решения. А делать надо, исходя из здорового умеренного консерватизма.
Потому что затащить в С++ систему типов, похожую на Хиндли-Милнера, например, — это было бы круто, но удовольствие очень дорогое.
Сделать void первоклассным типом — это удовольствие дешёвое, но возникают подводные грабли при обработке списка аргументов.

Но, прежде чем вводить в язык, надо прикинуть, как оно будет работать.

Допустим, к примеру, мы определим тип
namespace std {
  struct unit_t
  {
    unit_t() {}
    template<class T> unit_t(T const&) {} // любой тип можно привести к void, а значит, и к unit
  };
  static unit_t const unit;
}

std::unit_t foo(std::unit_t x, std::unit_t y, std::unit_t z) { return std::unit; }
std::unit_t v() { return std::unit; }

int main()
{
  foo( v(), v(), (v(),v(),v()) ); // третий операнд использует оператор запятую
}

Так, вроде бы понятно, как отличать последовательности от списка аргументов. Как в старом добром си, дополнительные скобки.
Недостаёт трёх вещей
— неявного приведения void к std::unit_t
— неявных return'ов в конце функций
— особой реализации оператора запятой, т.к. пользователь может определить свой оператор, который захавает unit_t, — но захавать чистый void не получится, — все operator,(void,T) и operator,(T,void) являются встроенными, — то же самое надо (?) и для unit_t

Это один путь.
Другой путь — не делать новый тип, а сразу принять void как первоклассный (т.е. внутренне реализовать его так же, как std::unit_t).
Тогда у нас встаёт проблема legacy — старый синтаксис
some foo(); // нуль-арная
some foo(void); // нуль-арная или, по-новому, унарная?

Волюнтаристский подход состоит в том, чтобы запретить старый стиль и трактовать всё сразу по-новому.
Резать-хвост-по-частям — в том, чтобы такие унарные функции домысливать до дефолтных параметров
some foo(); // имея в виду some foo(void _ = void());

foo();
foo( void() );
foo( bar ); // приводя значение bar к void
Перекуём баги на фичи!
Re[6]: Тип 'void'
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 27.05.14 05:57
Оценка: +2
Здравствуйте, NeoCode, Вы писали:

NC>
function foo(void, int, void);

NC>(такое может получиться, например, из шаблонной функции при подстановке void в параметры шаблона)
NC>Должен ли компилятор "сворачивать" ее к виду "foo(int)"?

Мой ответ: нет, ни в коем случае. От этого опять куча проблем и граблей.

И еще важный момент: нужно понимать, что f() — это не вызов функции без аргументов, а именно вызов с одним аргументом — (), он же void. Он потому так и называется (), не просто так. Поэтому f(void) не имеет никакого смысла, это же f (()), бессмысленная конструкция даже синтаксически.
А foo(void, int, void) принимает тупл из трех элементов, ни больше ни меньше.
Re[9]: Тип 'void'
От: Klapaucius  
Дата: 27.05.14 10:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

NC>>2. Функция, объявленная как foo(), эквивалентна функции foo(void), т.е. отсутствие аргументов вообще эквивалентно одному аргументу void.

S>Я думаю, это лишнее.

А чем тут не подходит ваш же собственный аргумент в пользу возвращения void

Опять же, всякие ФВП приходится писать дважды — для функций, возвращающих значение, и для невозвращающих значение.

просто исправленный на "всякие ФВП приходится писать дважды — для функций, принимающих значение, и для непринимающих значение"?
Да тут дело даже не в том, что их приходится писать дважды, но и некий обобщенный код не будет рассчитан на такие случаи, и если вдруг понадобится в такой обобщенный код передать функцию, которая не принимает ничего — придется самому вводить тип Unit с одним значением и оборачивать "непринимающие" функции.
По крайней мере в подавляющим большинстве ФЯ именно так и сделано, () — это значение тина unit, единственное населяющее этот тип (не считая _|_ в ленивых языках) — это проверенное временем решение.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[10]: Тип 'void'
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.05.14 03:49
Оценка:
Здравствуйте, Klapaucius, Вы писали:


K>А чем тут не подходит ваш же собственный аргумент в пользу возвращения void

K>

K>Опять же, всякие ФВП приходится писать дважды — для функций, возвращающих значение, и для невозвращающих значение.

K>просто исправленный на "всякие ФВП приходится писать дважды — для функций, принимающих значение, и для непринимающих значение"?
Тогда получается "...приходится писать бесконечное количество раз — для функций, принимающих 0 значений, 1 значение, 2 значения, и т.п."
А если ФВП способна работать с функцией, принимающей N аргументов, то она отлично справится и с N=0.
Если, конечно, логика ФВП этому не противоречит — например, каррировать функцию без аргументов уже не получится.

K>Да тут дело даже не в том, что их приходится писать дважды, но и некий обобщенный код не будет рассчитан на такие случаи, и если вдруг понадобится в такой обобщенный код передать функцию, которая не принимает ничего — придется самому вводить тип Unit с одним значением и оборачивать "непринимающие" функции.

K>По крайней мере в подавляющим большинстве ФЯ именно так и сделано, () — это значение тина unit, единственное населяющее этот тип (не считая _|_ в ленивых языках) — это проверенное временем решение.
Тогда проще отказаться от функций, не принимающих значение.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Тип 'void'
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 28.05.14 08:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Если, конечно, логика ФВП этому не противоречит — например, каррировать функцию без аргументов уже не получится.

S>...
S>Тогда проще отказаться от функций, не принимающих значение.

Именно так и надо поступить. Тогда и с каррированием проблем не будет: f () — вызов, f — каррированная ф-я.
Re[11]: Тип 'void'
От: Klapaucius  
Дата: 28.05.14 13:05
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Тогда получается "...приходится писать бесконечное количество раз — для функций, принимающих 0 значений, 1 значение, 2 значения, и т.п."

S>А если ФВП способна работать с функцией, принимающей N аргументов, то она отлично справится и с N=0.

ФВП, работающая с функцией одного аргумента a -> b отлично со всем этим справляется, несколько параметров можно определять так a -> (c -> d) или (c,d) -> b и так далее. Возращающую несколько значений — a -> (c,d) и так далее. Функцию с одним параметром, с областью определения в одно значение (а привычные функции foo() как раз такие) соответственно () -> b, с областью значений в одно значение a -> () (это всякие void foo функции). С пустой областью определений Void -> b (Void — это пустой тип, не нужно путать его с void из мейнстрим языков, который вовсе не пустой и вообще называется Unit), такие тоже находят применение см. http://hackage.haskell.org/package/void-0.6.1/docs/Data-Void.html#v:absurd

S>Тогда проще отказаться от функций, не принимающих значение.


Ну да, что такое "функция не принимающее значение" вообще не очень понятно.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[5]: Тип 'void'
От: NeoCode  
Дата: 29.05.14 13:35
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Автораскрытие кортежей это плохо.

_NN>Зато явно очень удобно (см. Python )

Кстати, можно ссылку или пример из python с этим раскрытием кортежей?
Re[6]: Тип 'void'
От: _NN_  
Дата: 01.06.14 19:48
Оценка:
Здравствуйте, NeoCode, Вы писали:

NC>Кстати, можно ссылку или пример из python с этим раскрытием кортежей?


Пожалуйста:
def f(a, b, c): print("a = {}, b = {}, c = {}".format(a, b, c))

x = (1,2,3)

f(*x)


a = 1, b = 2, c = 3
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Тип 'void'
От: NeoCode  
Дата: 21.06.14 20:59
Оценка:
Здравствуйте, _NN_, Вы писали:

Систематизирую организацию кортежей в различных языках. И вот какой вопрос с void возник совершенно неожиданно.

1. Если функция возвращает значение, а оно не используется — это нормально и менять это (заставлять использовать плейсхолдер для ненужного возвращаемого значения) крайне не хочется. Просто неудобно.
func foo() int { return 10;}
foo(); // теряем возвращаемое значение
_ = foo(); // так правильнее, но писать каждый раз лень!


2. По аналогии получается, если функция возвращает два и более значения (кортеж), то мы обязаны разрешить терять эти значения — иначе нарушится логика языка. Именно такое поведение реализовано например в Go.
func bar() int, float { return 10, 1.23; }
i, f = bar(); // ok
i = bar(); // ok, теряем второе значение
bar(); // ok, теряем все


3. Присваивание — обычная операция, ничем не отличающаяся от остальных. И экстраполируя поведение множественного присваивания на остальные операции, неизбежно получаем правило, что множественные операции выполняются над минимальным кортежем, участвующим в операции.
// '@' - некоторая бинарная операция
x @ 1,2,3; // выполняется x @ 1, остальное выкидываем
x,y,z @ 1; // выполняется x @ 1, остальное выкидываем


4. Поскольку void — это нуль-арный кортеж, то неизбежно получаем результат, что любые операции над void с точки зрения компилятора допустимы и просто игнорируются. Рассмотрим действия с переменной типа void:
_void_ = 1; // ok, ничего не происходит
x = _void_; // ok, ничего не происходит, значение x остается тем которое было до присваивания


5. Вообще говоря, из этого свойства следует очень интересное следствие: любые фрагменты выражений с участием переменной void следует игнорировать, а не ругаться на ошибки компиляции:
 x = y * (z + _void_); // x = y*z

что не есть хорошо, так как появляется возможность например воткнуть вызов функции, возвращающей void, в выражение; если раньше компилятор ругался, то теперь он проглотит и сократит выражение так чтобы ближайщая к void операция не выполнялась.

Собственно вот. К чему приводит лень — попытка облегчить жизнь введением исключения из правила привела в конце концов к усложнению этой же жизни в самом неожиданном месте
Re[2]: Тип 'void'
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.14 13:34
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>Как элемент мозгового штурма, а что если в языке object будет наследником от void?


Рак мозга будет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Тип 'void' - TypeScript
От: _NN_  
Дата: 10.08.14 13:33
Оценка:
В TypeScript на удивление как раз можно использовать void в параметрах обобщений:

interface DictionaryOrSet<K, V> {
  add(key : K, value : V) : void;
}

class Dictionary<K,V> implements DictionaryOrSet<K,V> {
  add(key : K, value : V) : void { }
}

class Set<T> implements DictionaryOrSet<T,void> {
  add(value : T) : void { }
}

var d = new Dictionary<number, string>();
d.add(1, "A");

var s = new Set<number>();
s.add(1);
http://rsdn.nemerleweb.com
http://nemerleweb.com
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.