LINUX.ORG.RU

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

 , qod, ,


4

5

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

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

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

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

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

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

в плюсах это пишется как std::optional < Error >.

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

в плюсах это пишется как std::optional.

std::optional не возвращает тебе ошибку. Он возвращает enum содержащий либо значение, либо ничего. Как только мы берем другую функцию, которая что-то возвращает, это перестает работать.

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

не понял. ты говорил, что твоя конструкция -

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

и теперь ты говоришь

std::optional не возвращает тебе ошибку. Он возвращает enum содержащий либо значение, либо ничего

а в чем разница? и там и там возвращается или мусор под названием - ничего, или некая «ошибка» например число или строка.

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

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

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

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

а в чем разница? и там и там возвращается или мусор под названием - ничего, или некая «ошибка» например число или строка.

Неа.

enum Option<T> {
   Some(T),
   None
}

То есть функция возвращает Option. Это вполне себе определенный тип.

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

Потому что если функция возвращает что-то осмысленное (число, строку, файловый дескриптор), то Option бесполезен для возврата ошибки, ибо второе поле у не позволяет передать собственно ошибку. То есть либо получишь Some(T), либо None, который скажет «я не шмогла». Это плохой API для ошибок.

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

«не надо множить сущности без необходимости»

Вот как раз в шаблонный тип Result @gaylord влазят и функции, которые что-то возвращают, и которые ничего не возвращают, а вы с std::optional предлагаете для первых использовать один тип, а для вторых - второй.

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

На самом деле, если бы разработчики Rust с самого начала признали себе что ошибки должны быть частью языка (примерно то, что происходит с Pin сейчас), мы могли получить что-то такое:

fn (x: i32)           // не возвращает ничего
fn (x: i32) -> !Error // не возвращает ничего или возвращает ошибку

Под капотом был бы тот же самый Result, но при этом нам не нужно было бы постоянно все врапить в Ok.

И был оператор возврата ошибки:

fn (x: i32) -> !Error {
   return !Error::new("caboom");
}
gaylord
()
Последнее исправление: gaylord (всего исправлений: 1)
Ответ на: комментарий от gaylord

То есть у функция возвращает Option. Это вполне себе определенный тип.

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

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

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

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

Что значит какие угодно? () это тоже определенный тип. Он просто пустой.

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

Ты опять не понял что происходит. Все типы определены. Пожалуйста, перечитай тред ещё раз.

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

Вот как раз в шаблонный тип Result @gaylord влазят и функции, которые что-то возвращают, и которые ничего не возвращают,

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

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

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

Так мы с этого начали:

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

Похоже ты просто не читаешь что тебе пишут и шизофазируешь на свободную тему.

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

Вот как раз в шаблонный тип Result @gaylord влазят и функции, которые что-то возвращают, и которые ничего не возвращают, а вы с std::optional предлагаете для первых использовать один тип, а для вторых - второй.

это не поддается расшифровке.

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

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

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

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

Но мнение имеешь. Волшебно!

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

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

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

Но мнение имеешь. Волшебно!

але, мы не руст обсуждаем-то. его даже в тегах нету.

Зачем тебе что-то доказаывать

ну и не надо доказывать. значит доказательства необходимости void типа пока мы не видели.

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

але, мы не руст обсуждаем-то. его даже в тегах нету.

Тогда зачем тебя понесло доказывать что () в Rust не нужен?

ну и не надо доказывать. значит доказательства необходимости void типа пока мы не видели.

Мы это кто? Мы с @wandrien видели.

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

Тогда зачем тебя понесло доказывать что () в Rust не нужен?

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

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

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

Последнюю страницу говоришь.

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

э не не

как структурное прог на асме возможно (ежи кактус вот это всё)

так и риальне ооп и т/д в сяшке

так и лиспование на основе сяй в самой ся

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

а так как ся это ассемблер(«легкопереносимый» отцами) с приятным ака структурным синтаксисом

то этот асм позволяет

всякую лямбду задать как указатель на указательную пару (ну али струтуру пары указателей) - где первый это ук на код , вторая указателька на данные - ака контекст лябды

подробности о троллейбусах у хлебушков во всяких текстах пипо «ооп на си» али «real functional programming on c»

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

Вот есть юнит тайп (у тебя nulltype), отлично подходит для описания процедур void f(…)

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

Но набор допустимых операций - разный.

Основное назначение nulltype - это обозначение пустоты в ситуации работы с указателями. Тип указателя это дизъюнкция а ля NullablePointer(T) = Pointer(T) | nulltype. Поэтому если у нас есть указатель x и функция nulltype f(), то запись x = f(); будет корректна.

А в случае с void функциями это не то, что мы хотим получить.

Далее, также возможна и функция с сигнатурой void g(nulltype).

И в таком случае запись g(f()) будет корректна.

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

Вы неправильно используете слова «пустой тип». Пустой тип - это тип, в котором нет термов. Т.е. например bottom, never enum {}. () - это терм типа Unit (или () в хаскелле - там решили этот тип записывать так же, как его единственный терм). Когда вы говорите, что функция ничего не возвращает, это можно понять двумя способами.

Либо как она не возвращает ничего информативного, но какое-то значение всё-таки возвращает, как в случае Rust Result<(), Error> или хотя бы по смыслу, как в сишном void.

Либо что она буквально не может вернуть значение - потому что в место своего вызова не возвращается (зацикливается или бросает исключение), а весь код после неё мёртвый. Эту ситуацию можно описать как возврат значения типа пустого enum, что невозможно. Т.е. функция буквально ничего не вернёт.

Называя тип () пустым, вы путаете себя и других.

anonymous
()

Близким аналогом void, кстати, выступает тип, порождаемый через namespace.

namespace-тип можно присваивать другим именам и сравнивать с другими типами. Инстанцировать его нельзя, размера он не имеет.

namespace void2 of end;

type v1 = void;
type v2 = void2;

static_assert(v1 == void);
static_assert(v2 == void2);
static_assert(v1 != v2);
static_assert(void != void2);


void2 f(); /* Ошибка */
void f(void2); /* Ошибка */
wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 2)
Ответ на: комментарий от wandrien

Возможно, вы всё это знаете, но давайте синхронизируемся и поррасуждаем.

Мы знаем, что все юнит тайпы изоморфны (т.е. юнит тайп единственный с точностью до изоморфизма). С номинативной типизацией есть возможность построить множество разных изоморфных юнит тайпов.

type MyUnit1 = MyUnit1
type MyUnit2 = MyUnit2

Обычно есть смысл делать несколько одинаковых (отличающихся лишь обёрткой), но не равных типов в ситуациях а-ля kg<float> + position<float>. В случае юнит тайпа я аналогичного полезного кейса прям сходу придумать не смог (могу только в рамках дамми типов для всякой типовой астронавтики).

При этом на самом деле у нас довольно много разных юнит тайпов внутри других типов. Хаскеллевый Nothing имеет тип не (), а forall a. Maybe a. Или даже List a = (a, List a) | EmptyList. (Тут EmptyList тоже имеет тип forall a. List a.). Ну и самое смешное, Bool = True | False, где True имеет тип Bool.

И так далее. У нас нет отдельного юнит тайпа c названием True и c единственным омонимичным термом True.

Вернёмся к кейсу вашего nulltype. Почему не назвать это Nullptr, и буквально засунуть его внутрь типа NullablePointer<a> = Pointer<a> | Nullptr? Тут же получить null safety. Так сделано в котлине (с поправкой на всратость реализации енумов в джава-мире), свифте, фшарпе и расте (список не полный).

Тогда (void *) будет NullablePointer<T: Any>, просто void - это Unit (aka ()), а Never это честный Never (как в свифте и расте). например https://github.com/rust-lang/rfcs/pull/1216

Для всего этого теория довольно разработанная и устоявшаяся.

Мой изначальный вопрос был про два боттома, но у вас это не два боттома (void и Never), а два юнита (nullptr и ()), по смыслу.

В случае боттомов всё всегда сложнее. В ленивых языках боттом - это значение, да ещё (взмахивая руками) «любого типа». В энергичных это зачастую честный Never, без значений. И там и там произносятся похожие слова про невозвращаемость, но я не понимаю до конца, как обо всём этом думать. В теории типов, с которой я знаком, этот вопрос обходится стороной, по крайней мере при первом знакомстве. Дальше там как будто можно нагородить конструкции из refl’ов, но поля слишком узки.

Можно продолжать думать о типе экспрешена return 5 внутри x=(return 5) как о типе Never, тут всё ок.

Просто в случае двух боттомов у меня есть непонятка, почему например в расте Never может быть приведён к любому типу, а пустой енум нет.

Мне было в это сложно вникнуть и возможно я не всё понял, но причина введения двух боттомов - в растовой конструкции unsafe которая и создаёт unsoundness: https://smallcultfollowing.com/babysteps/blog/2018/08/13/never-patterns-exhaustive-matching-and-uninhabited-types-oh-my/

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

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

https://en.cppreference.com/w/cpp/types/nullptr_t

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

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

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

Почему не назвать это Nullptr, и буквально засунуть его внутрь типа NullablePointer = Pointer | Nullptr?

Потому что фактически это так и есть, но в языке с системой типов уровня Паскаля нет возможности выразить эту мысль.

Поэтому я иду снизу, от реализации: сначала в ЯП будут введены аналоги NullablePointer<a> и Pointer<a> в качестве типов из коробки, а уже потом - средства выражать идентичные концепции на языке типов.

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

В функциональной семантике выражение, типом которого является nulltype, является таким же законным выражением, как и любое другое. А поскольку nulltype заведомо не требует никакого вычисления, тело выражения может быть сразу свёрнуто до null.

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

Разрешение этого несоответствия требует введения понятия чистоты функции в ЯП.

В случае боттомов всё всегда сложнее. В ленивых языках боттом - это значение, да ещё (взмахивая руками) «любого типа». В энергичных это зачастую честный Never, без значений. И там и там произносятся похожие слова про невозвращаемость, но я не понимаю до конца, как обо всём этом думать. В теории типов, с которой я знаком, этот вопрос обходится стороной, по крайней мере при первом знакомстве. Дальше там как будто можно нагородить конструкции из refl’ов, но поля слишком узки.

Это на слишком сложном, пожалуй. Тут я пасс. Трактовка недостижимого значения как значения «любого типа» по смыслу заставляет вспомнить импликацию. Если посылка ложна, то суждение истинно при любом следствии.

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

При чем понятия «формула» и «вычисление» тут первичны, а исполнение сугубо вторично.

В языках наподобие C++ и C# при кажущейся похожести пассов руками первичным понятием является именно «исполнение», а не «вычисление». А «вычисление» возникает вследствие утилитарной попытки (порой тщетной) объяснить компилятору предметную область задачи. (В ЯП наподобие Python/Ruby/SmallTalk — вообще не возникает.)

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