LINUX.ORG.RU

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

 , qod, ,


4

4

Финально определился с названием языка, подчистил разные хвосты и написал 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)
Ответ на: комментарий от wandrien

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

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

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

Если вы понимаете под функциональной семантикой то, как устроены ленивые программы на хаскеле, то вы правы по определению. Но даже в хаскеле есть возможность указать strict evaluation (в виде восклицательного знака или используя seq которая будет вычислять даже то, что должно иметь юнит тайп. По-моему, это не особо интересно:

import System.IO.Unsafe as SU

f::Int->Int
f x = SU.unsafePerformIO $ putStrLn "Hello" >> return 42

getUnit :: Int -> ()
getUnit x = ()

main = do
    putStrLn $ show $ getUnit $  f 1
    putStrLn $ show $ getUnit $! f 1

()
Hello
()

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

Естественно, в расте, где старались следовать «всё есть выражение», это тоже всё не представляет конфликта.

На счёт из лжи следует что угодно - вы правы, это связано. Надеюсь вы для себя тут что-нибудь почерпнёте: https://counterexamples.org/anythings.html

А на счёт первичных понятий… Всё довольно зыбко. Между си и хаскеллем есть много промежуточных по своей вычислительно-исполнятельности языков, и это скорее линейная комбинация, чем выпуклая сумма.

anonymous
()

Блин, башка сегодня не варит весь день. Вчера слишком допоздна работал с кодом, видимо.

Сижу смотрю на код: знаю, что надо сделать, как надо сделать, но сил делать нет.

На улице какой-то химией несёт с промки, тоже по мозгам давит еще.

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

На улице какой-то химией несёт с промки, тоже по мозгам давит еще.

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

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

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

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

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

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

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

Я к тому что авторам языков денег дают налево и направо.

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

alysnix ★★★
()

Кстати пример из документации Раста, который демонстрирует, что noreturn это «значение любого типа»:

fn main() {
    fn sum_odd_numbers(up_to: u32) -> u32 {
        let mut acc = 0;
        for i in 0..up_to {
            let addition: u32 = match i%2 == 1 {
                true => i,
                false => continue,
            };
            acc += addition;
        }
        acc
    }
    println!("Сумма нечётных чисел до 9 (исключая): {}", sum_odd_numbers(9));
}

Буду ли я позволять миксовать statement-ы внутрь выражений - вопрос открытый. Скорее нет, чем да. Поэтому у меня noreturn в таком виде работает только на уровне функции как целого.

wandrien ★★
() автор топика

Отдельно замечу, насколько же всратый синтаксис у Раста с этими скобочками. Мало индустрии было 50 лет сишки, они решили усугубить.

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

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

Ну напиши ты нормально, харэ писать в стиле хакеров из 60-х, у тебя не терминал 40x25 и не 128 КБ памяти! Хотя бы так:

function main():
    function sum_odd_numbers(up_to: u32) -> u32:
        let mut acc = 0;
        for i in 0..up_to do
            let addition: u32 = (match i%2 == 1 of
                case true => i,
                case false => continue,
            end);
            acc += addition;
        end
        return acc;
    end
    println!("Сумма нечётных чисел до 9 (исключая): {}", sum_odd_numbers(9));
end
wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

в конце времени исполнения как отражение оставить на стеке последнее вычесленное - как раз таки «экономия на спичках» как fallthrow vs break для свича в разных потомков си-синтаксиса

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

Ну напиши ты нормально, харэ писать в стиле хакеров из 60-х

Ответ на: комментарий от wandrien 22.12.24 15:55:45 JST

Сами с собой ругаетесь?

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