LINUX.ORG.RU

Qod. Опубликовал исходники компилятора, над которым работаю

 , qod, ,


4

5

Финально определился с названием языка, подчистил разные хвосты и написал README. Теперь наконец-то можно посмотреть на нечто большее, чем просто фрагменты кода в постах на форуме: https://github.com/wandrien/qod/

Драфты по дизайну языка пока еще не готовы. Если перед НГ завала работы не будет, то может выложу их в течение пары недель. Черновики пишу на русском, осилить всё чётко сформулировать на английском в разумные сроки я точно не смогу. На русском-то не всегда получается.

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

А пока можно посмотреть на сам код вживую.

Ответ на: комментарий от wandrien

разраб языка, который не слышал про динамическую проверку типа, явно дальше си двигаться не собирается.

а если у него вдруг появятся вирт методы.. вдруг окажется, что ему нужно vmt.

а если у него будет еще и множественное наследование, то у него появится много еще интересных штучек от типа в бинарнике.

но это совсем другая история, правда?

ты же хотел победить с++? а рассуждаешь так, будто си для тебе предел.

alysnix ★★★
()
Ответ на: комментарий от wandrien

В объявлении переменных не путаешься?

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

alysnix ★★★
()
Ответ на: комментарий от wandrien

Запись type t = [10]char; это ровно такая же запись как int x = 2 + 3;.

раз у тебя нет грамматики, а «type» у тебя обьявляется типом, то вопрос - как парсится такая запись?

type T = type;

или такая

type T = type[10]
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

разраб языка, который не слышал про динамическую проверку типа, явно дальше си двигаться не собирается.

а если у него вдруг появятся вирт методы.. вдруг окажется, что ему нужно vmt.

Я же говорил, что ты не очень-то умный.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

Это синтаксическая ошибка.

почему? я обьявил тип. чем он отличается от обьявления?

type T = int;

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

примечание. тип всех типов - это не тип. это «категория». и попытка его засунуть в типы приведет только к путанице.

alysnix ★★★
()
Ответ на: комментарий от alysnix

почему?

Потому что операция «обращение к элементу по индексу» к type не применима, это не массив.

или массивы, указатели, можно брать не от всех типов? а почему?

Подумай. Попробуй подумать, возможен ли массив от void или от функции. Как надумаешь, поделись.

примечание. тип всех типов - это не тип. это «категория». и попытка его засунуть в типы приведет только к путанице.

Момент, когда alysnix открывает для себя понятие зацикленности метакласса… а нет, не открывает.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

понятие зацикленности метакласса…

это сильно. приведи пример.

Попробуй подумать, возможен ли массив от void или от функции.

ты сишнег что-ли? void - это чисто сишное изобретение, это вообще не тип. и void* - это не указатель на void, это просто мнемоника нетипизированного указателя. в более корректных языках такой тип обычно называют address. и никаких void там нет в принципе

а функциональный тип - это технически указатель на функцию. потому от него можно брать все что угодно.

alysnix ★★★
()
Ответ на: комментарий от wandrien

Компилятор будет на это ругаться так же, как на void x[16], но формально, единственная причина так делать, это отсуствтие лямбд. Если бы лямбы были, то массив из функций вполне бы имел смысл.

typedef int(fn_t)(int a);

int main(void)
{
	fn_t x[16];
	return 0;
}
gaylord
()
Ответ на: комментарий от alysnix

void - это чисто сишное изобретение, это вообще не тип.

Я иногда жалею, что на LOR войс-чата нет, такие угары лучше голосом обсуждать.

Кстати, назвать лоровский voice-чат можно было бы «void-чат»)))

и никаких void там нет в принципе

Автор тысячи компиляторов))))

https://en.wikipedia.org/wiki/Empty_type

а функциональный тип - это технически указатель на функцию

«ты сишнег что-ли?» (c)

wandrien ★★
() автор топика
Ответ на: комментарий от gaylord

Если бы лямбы были, то массив из функций вполне бы имел смысл.

Неа. Дело в том, что массив в Си/Си++ - это последовательность элементов фиксированного (и ненулевого) размера. А для функции не существует понятия размера.

«Массив лямбд», конечно, возможен, но надо понимать, что в таком случае это будет и «массив» — уже не сишный массив, и «лямбд» — уже не сишных функций.

wandrien ★★
() автор топика
Ответ на: комментарий от gaylord
typedef int(fn_t)(int a);

int main(void)
{
	fn_t* x[16];
	return 0;
}

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от wandrien

Неа. Дело в том, что массив в Си/Си++ - это последовательность элементов фиксированного (и ненулевого) размера. А для функции не существует понятия размера.

Потому что у нас нет лямбд.

«Массив лямбд», конечно, возможен, но надо понимать, что в таком случае это будет и «массив» — уже не сишный массив, и «лямбд» — уже не сишных функций.

Почему? У нас есть массив из указателей на функцию и все ок.

Вот, зацени Rust:

fn main() {
    let table = [
        |_x: i32| return 0,
        |_x: i32| return 1,
        |_x: i32| return 2,
        |_x: i32| return 3,
    ];
    println!("{}", table[2](1337));
}

Здесь table это [fn(i32) -> i32]. С размером 32 байта, потому что лямбы сделаны через указатели. Но это детали реализации и к системем типов мало отношения имеют. А массивы в Rust имеют плюс-минус ту же семантику, что и в C.

gaylord
()
Ответ на: комментарий от wandrien

ты читать то умеешь свои же ссылки, писатель.

A type theory need not contain an empty type. Where it exists, an empty type is not generally unique.[2] For instance, T → 0 {\displaystyle T\to \mathbb {0} } is also uninhabited for any inhabited type T {\displaystyle T}. 
alysnix ★★★
()
Ответ на: комментарий от gaylord

Указатель на функцию это не то же самое, что функция.

В компилируемых языках, как правило, сам тип функции - это чистая абстракция для тайпчека, а фактическая работа идёт либо с константными значениями указателей на функцию, либо с переменными, имеющими тип указателя на функцию.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

Указатель на функцию это не то же самое, что функция.

Да, я знаю.

В компилируемых языках, как правило, сам тип функции - это чистая абстракция для тайпчека, а фактическая работа идёт либо с константными значениями указателей на функцию, либо с переменными, имеющими тип указателя на функцию.

Да, но мы тут про систему типов говорили. В C есть явная разница между int (x)(void) и int (*x)(void) – одно есть порождение другого. В том же хрусте такого нет, там все функции есть fn() -> i32. То есть для хруста массив из функций это валидная синтаксическая конструкция. Но это исключительно ментальная гимнастика, понятно что внутри адреса.

gaylord
()
Ответ на: комментарий от gaylord

У каждого языка своя система типов.

Но в любом случае, в компилируемом языке основное предназначение функции как типа - это проверка совместимости типов при вызове функции.

А что функция может быть скомпилирована в машинный код, и адрес её начала может быть представлен как указатель, — это уже более узкое явление.

Например, функция может существовать только на этапе компиляции, и в принципе не может быть представлена в машкодах. (В Qod такие функции это sizeof(), countof() и т.п. Кстати чтоб формально описать тип аргумента у этих функций, текущей системы типов языка не достаточно, необходимо её совершенствование.)

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от gaylord

я вообще сишные конструкции обсуждать не буду, язык это древний, специфический, местами странный и не является эталоном, но кое-что оттуда взять можно.

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

если б прототип писался

fun aaa(param: type): result_type;

то void был бы не нужен. потому что функция без результата писалась бы:

fun aaa(param: type);

и ввели еще void* как беcтиповой указател на байт. что в других языках называется address.

alysnix ★★★
()
Ответ на: комментарий от alysnix

void *p буквально означает, что при разыменовании p ты получаешь «ничего», «нет значения выражения».

Но для людей, не умеющих в абстракции, понять такое сложно.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

void *p буквально означает, что при разыменовании ты получаешь «ничего», «нет значения выражения».

это безтиповой указатель. и может указывать на что угодно. если конечно не null. и при его разыменовании возникает значение неопределенного типа. а не «ничего».

«ничего» возникает при разыменовании nullptr. что есть UB.

не видишь разницы между ничего и что угодно, логик?

alysnix ★★★
()
Ответ на: комментарий от alysnix

и может указывать на что угодно

А какой набор допустимых операций определён над этим «что угодно»?

UPD: И какая область значений, за одно.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

А какой набор допустимых операций определён над этим «что угодно»?

а допустимых операций вне конкретного типа не существует. впрочем что там у тебя в голове, я не знаю.

что ты меня спрашиваешь? это у тебя есть void, nulltype, и noreturn. было б интересно посмотреть подробности этого винигрета.

еще раз напомню, что даже вики говорит, что void необязателен, noreturn это какой-то спецификатор функции, а не тип. a nulltype - это калька с плюсов, этот тип тоже не нужен по сути.

alysnix ★★★
()
Ответ на: комментарий от alysnix

а допустимых операций вне конкретного типа не существует

А штука, которая описывает множество допустимых операций называется… называется… помощь зала??

noreturn это какой-то спецификатор функции

Ух ты.

А почему тогда вот такая программа не проходит тайп чек, а программа во втором примере проходит?

void foo()
  pass;
end

int bar(int x)
  if x == 0 then
    foo();
  else
    return x + 1;
  end
end
noreturn foo()
  while true do
    pass;
  end
end

int bar(int x)
  if x == 0 then
    foo();
  else
    return x + 1;
  end
end
wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

А почему тогда вот такая программа не проходит тайп чек, а программа во втором примере проходит?

это какой язык вообще? твое что-ли поделие? и ты спрашиваешь почему оно проходит или нет тайпчек… вообще не описав ни грамматики, ни семантики?

если у тебя синие таблетки кончились, пей красные уже

alysnix ★★★
()
Ответ на: комментарий от wandrien

Назвать тип у оператора return x + 1; в примере выше.

у тебя ни слова в доках про «операторы» нету. и их «типы». они существуют только в твоей голове. а я за нее не отвечаю.

alysnix ★★★
()
Ответ на: комментарий от alysnix

я вообще сишные конструкции обсуждать не буду, язык это древний, специфический, местами странный и не является эталоном, но кое-что оттуда взять можно.

Тогда зачем ты начал это делать?

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

Его много где ввели в систему типов по этой жи причине. См. unit в Haskell и Rust. Когда функция не возвращает ничего, это, внезапно, тоже надо как-то представлять в системе типов.

и ввели еще void* как беcтиповой указател на байт. что в других языках называется address.

В Go эта же задача решается через пустой интерфейс. Оно везде костылями обложено, void * ваще ничем не лучше и не хуже interface{}. Если в твоей системе типов нет аналога тайпклассов, ты в это упрешься в 100% случаев.

gaylord
()
Ответ на: комментарий от gaylord

Когда функция не возвращает ничего, это, внезапно, тоже надо как-то представлять в системе типов.

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

то есть вы сначала свели их к общей категории, а потом стали вводить нулевой тип, чтобы они отличались.

alysnix ★★★
()
Ответ на: комментарий от alysnix

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

Эм, нет, это все здесь вообще не причем. Смотри:

fn (i32) -> Result<(), Error>

Как ты опишешь это без void, () или аналогичной структуры?

gaylord
()
Ответ на: комментарий от wandrien

Некоторым чудом усмотрел разумное зерно в клоунаде выше (нет, не в претензиях к when, а в более серьёзном вопросе).

Можешь объяснить, зачем используется два разных ненаселённых типа? Вот есть юнит тайп (у тебя nulltype), отлично подходит для описания процедур void f(...). Вот есть ненаселённый bottom (у тебя noreturn), отлично подходит для, собственно, неконструируемых значений - ретурнов нетерминирующихся функций. Это и по Карри-Говарду, и по BHK-семантике.

Я не понимаю для чего у тебя и у (например) раста сделано два боттома. Можешь привести какие-нибудь ссылки и/или соображения, почему empty enum не отождествляют с bottom? Какие-то проблемы с soundness?

anonymous
()
Ответ на: комментарий от gaylord

Как ты опишешь это без void, () или аналогичной структуры?

а что это значит? тип результата - некая инстанциация шаблона с нулевым типом и каким-то Error? то есть в такой форме вы там записываете возврат просто ошибки?

ну это все равно что описывать одноногого инвалида как двуногого, но с нулевой ногой. математически точно же?

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 3)
Ответ на: комментарий от alysnix

а что это значит? тип результата - некая инстанциация шаблона с нулевым типом и каким-то Error? то есть в такой форме вы там записываете возврат просто ошибки?

Представь множество значений следующего вида:

enum Result<T, E> {
   Ok(T),
   Err(E),
}

Теперь представь, что у тебя есть функция, которая либо ничего не возвращает, либо выходит с ошибкой. Как описать тип функции без void и ()? Только давай без дамагогии про одноногих, пожалуйста, это технический раздел.

gaylord
()
Ответ на: комментарий от gaylord

функция, которая либо ничего не возвращает, либо выходит с ошибкой

что значит - ничего не возвращает или ВЫХОДИТ с ошибкой? то есть возвращает ошибку, например на вершине стека(технический же форум) или на вершине стека не находится ничего?… а это облом. потому что вызывающий не понимает, надо ли со стека снять значение или нет. это если не витать в облаках формальных скобочек. а если он не знает был ли реально возвращен результат - он не может правильно завершить вызов функции.

или тут о другом?

то есть, такого рода конструкции некорректны, ибо не могут быть тривиально реализованы, и являются двусмысленными. даже то, что вызов процедуры это statement, а вызов функции - это фактор в expression, то есть они применяются вообще в разных правилах, должен наводить на мысль, что их мешать нельзя.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

что значит - ничего не возвращает или ВЫХОДИТ с ошибкой?

Это значит нет никакого результата, котороый можно было бы использовать в бизнес-логике приложения.

Вот тебе пример такой функции:

fn create_file(x: i32) -> Result<(), Error> {
    File::create(format!("/tmp/foobar.{}", x))?;
    return Ok(());
}

Создали файл и вышли из функции. Все. Ничего полезного эта функция вернуть не может (это описывается типом ()).

то есть возвращает ошибку, например на вершине стека(технический же форум) или на вершине стека не находится ничего?… а это облом. потому что вызывающий не понимает, надо ли со стека снять значение или нет. это если не витать в облаках формальных скобочек. а если он не знает был ли реально возвращен результат - он не может правильно завершить вызов функции.

На стеке очевидно будет та структура, что я привел выше.

то есть, такого рода конструкции некорректны, ибо не могут быть тривиально реализованы, и являются двусмысленными. даже то, что вызов процедуры это statement, а вызов функции - это фактор в expression, то есть они применяются вообще в разных правилах, должен наводить на мысль, что их мешать нельзя.

Это тривиально реализуется.

gaylord
()
Последнее исправление: gaylord (всего исправлений: 2)
Ответ на: комментарий от gaylord

Это тривиально реализуется.

это реализуется нетривиально. это простых терминах реализуется как функция ВСЕГДА возвращающая вариантную запись, и вызывающий по тегу должен определить - возвратила ли она некий Error или мусор в виде void.

отсюда простой вывод. вы возвращаете вариантную запись с тегом безо всякхих там пустых типов. а пустые типы у вас возникли из-за ваших абстракций.

пустые типы возникают не тут, а в алгебрах типов, в результате формальных операций, результат которых, иначе как пустым типом и не описать. например в каких-нить «пересечениях типов», когда такое «пересечение» будет пустым.

alysnix ★★★
()
Ответ на: комментарий от alysnix

это реализуется нетривиально. это простых терминах реализуется как функция ВСЕГДА возвращающая вариантную запись, и вызывающий по тегу должен определить - возвратила ли она некий Error или мусор в виде void.

Ага, она так и реализуется.

отсюда простой вывод. вы возвращаете вариантную запись с тегом безо всякхих там пустых типов. а пустые типы у вас возникли из-за ваших абстракций.

Там (). Это пустой тип. Ещё раз прочитай код.

P.S. Процитирую разработчиков Rust, чтобы тебе было попроще:

The () type has exactly one value (), and is used when there is no other meaningful value that could be returned.

gaylord
()
Последнее исправление: gaylord (всего исправлений: 2)
Ответ на: комментарий от gaylord

Ага, она так и реализуется.

во! а вариантной записи пустой тип не нужен. она даже не может его инстанцировать. то есть будет вариантная запись с двумя тегами, и одним полем - Error, поскольку void не инстанцируется.

то есть void тут - просто некая закорюка, нужная для синтаксического сахарка в виде твоего енума, прямо отображаемого на вариантную запись.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

то есть void тут - просто некая закорюка, нужная для синтаксического сахарка в виде твоего енума, прямо отображаемого на вариантную запись.

Ага. Это не отменяет того, что в системе типов есть (), который используется, когда функция ничего не возвращает. Переходим обратно к моему изначальному вопросу: как написать сигнатуру функции без void, () и аналогов?

это какой язык вообще?

Rust, Haskell и ещё несколько функциональных языков.

gaylord
()
Последнее исправление: gaylord (всего исправлений: 3)
Ответ на: комментарий от alysnix

а разработчки rust это икона стиля? ну какие-то там разработчики со своими заморочками. :)

Ты уже четвертый комментарий юлишь и никак не можешь показать аналога Result<(), Error>. Пока что разработчики Rust выигрывают.

gaylord
()