Comments 58
А зачем вы мне это прислали? Думаете я не знаю? Команда Rust молодцы, что тут сказать.
Но я думал вы что-то своё покажете, а не чужую работу. Интересно было бы посмотреть на ваши личные компиляторные наработки.
Ну раз уж сами попросили :) Вот платформа lsFusion. Свой язык, открытая платформа. У нас свойства и действия объявляются отдельно от классов, есть множественное наследование и диспетчеризация по классам всех параметров. Более того, вообще нет обычной записи через точку.
Но в целом, зачем вообще привязывать функцию к одному «главному» объекту ? В Вашем же примере расстояние считается между двумя точками. Чем первая принципиально заслужила владеть этой операцией больше второй ?
Например, остаток товара на складе на lsFusion можно описать так:
CLASS Sku 'Товар';
CLASS Stock 'Склад';
balance 'Остаток' = DATA NUMERIC[14,3] (Sku, Stock);У свойства два параметра. Зачем выбирать, в какой класс его запихнуть — в товар или в склад ?
А вообще, чтобы сослаться на Rust, обязательно сначала свой компилятор написать ? Вы бы лучше объяснили, чем Ваш подход отличается. Наличие своего компилятора у собеседника на это никак не влияет.
Выдумывать отдельные языки для прикладных задач, следом искать похвалы на форумах и конечно агрессивно защищать свою бурду - анимешник на лицо. "Подписаться не забудь, я там распальцовку для ссылки на объект показываю"
Ссылка по делу, и идея куда старше Rust: непрозрачный FILE* в C играл роль объекта с методами ещё в семидесятых, а fopen/fread/fclose – его интерфейс. Rust честно развёл данные и поведение: структуры хранят поля, методы живут отдельно. Позднее связывание включается осознанно, через объекты трейтов, и цена видна сразу – рядом с данными едет таблица вызовов.
и раскрываю все тайны IT‑индустрии!
Это видимо была первая: мы научились считать квадрат расстояния в программе! Огромный прогресс однако!
Миллениалы эдак С изобретут
Что-то пытаюсь понять но суть ускользает. Я правильно понимаю, что объявлена точка (point) и вызван ее метод? Т.е. у точки есть поведение и состояние? Т.е. point по сути является экземпляром класса Point несмотря на отсутствие слова "class"? В общем суть изобретения не ясна.
Насколько проще стало без классов
...
Отсутствие классов в компиляторе заставляет его автора оставить какой-то способ связать данные с функциями.
Выглядит так, что Вы сами создали себе ограничение и придумали велосипед для его обхода, вернувшись в исходную точку.
Можно объявить ещё один тип
Coordinatesс теми же полямиxиyи передать его вlengthSquared. Другое имя само по себе не делает данные несовместимыми.
В ООП их просто наследовали бы от единого суперкласса.
К нашему
lengthSquared(p: Point)можно добавить второй вариант — для квадрата расстояния до другой точки:
Исходная функция с одним параметром это частный случай расстояния до нуля координат. Зачем тут перегрузка, когда можно обойтись default value.
Я удалил классы, но оставил объекты с методами
Это неправда, у вас именно что классы, только вместо ключевого слова class используется type, а синтаксис объявления метода сделан как частный случай объявления функции. Грубо говоря, нельзя сделать функцию, принимающую type как первый аргумент, и не создать при этом метод. Чего в этом хорошего, я хз. Выглядит как чисто синтаксическое упражнение.
Вы не правы, это не классы, а протоколы
И в чём отличие? Ну кроме как расстояние по Левенштейну в 8?
Отличия фундаментальные, странно что вы их не понимаете.
Протоколы - это конструкт статического анализа, который появляется и исчезает на уровне текста программы. Это у меня в языке есть.
Классы - материализация в памяти, которая протекает на низкие уровни и тащит за собой конструкты в духе таблиц виртуальных методов или заголовков синхронизации. Этого у меня в языке нет.
Я тоже не понимаю, ни VT ни синк примитивы не обязаны быть в классах, по факту класс это тот же конструкт статического анализа.
Как не назови хоть type хоть class по сути одно и то же, разметка памяти.
А внешние методы ровно то же, что и методы класса, просто функция первый аргумент которой ссылка на объект типа.
Хотите методы без классов - идите в Раст. Там это всё очень хорошо математически продуманно.
Поздравляю, вы открыли утиную типизацию
Работа сама по себе классная — разобрать метод до “функция + receiver + резолв по имени типа” и собрать это в рабочий компилятор не так просто, respect. Но по применимости не уверен: без traits/interfaces (как в Rust или Go) структурная проверка работает “в одну сторону” — метод не увидит literal без явной аннотации, и полиморфизма тут не появляется. По сути это function(receiver, ...) с доп. слоем резолва через точку, без выгод, ради которых обычно и отказываются от классов. Как учебный проект — топ, но какую реальную задачу это решает лучше, чем прямой вызов функции?
Пример решения реальной задачи https://habr.com/ru/articles/1010530/
Тоже занимался подобной идеей, но столкнулся со множеством проблем, когда ЯП начинает обрастать "мясом".
Например, как сделать так, чтобы два объекта имели разные методы, что-то типа myCube.volume() и mySphere.volume()? Получается, что нужно будет разрешать перегрузку функций по типу аргумента, но тогда что делать, если перегрузки конфликтуют друг с другом, что если несколько одинаковых функций импортируются из других модулей, и...
В общем, на синтетических примерах идея кажется простой до гениальности, а в реальном рабочем ЯП превращается в глубокую кроличью нору костылей.
Что-то вроде классов типов в Haskell?
Ага, так и хотелось сказать что это type class на минималках.
Я тоже пробовал свой язык дизайнить и кажется, что если есть типы-суммы с тегами и тайпклассы, то как будто можно построить адекватный язык без наследования. Отличия конечно будут, но мне понравилось что получилось, и штуки типа vtable и прочего если очень надо - можно эмулировать.
Я больше скажу. Всё это кажется смешно и просто, пока сам с нуля не напишешь
Хотелось, чтобы эти вещи работали без обязательной упаковки в класс.
зачем? как будто классы как раз придуманы, чтобы хранить рядом данные и методы работы с ними - для более простой организации кода.
Мне структурная типизация из TS тоже видится более элегантной, чем то, что мы имеем в шарпе с явным наследованием
Картинка с троллейбусом из буханки
Уберём аннотацию у переменной и вызовем lengthSquared(point) напрямую. Получим те же 25. Мы нигде не объявляли, что литерал что‑то реализует. Его поля уже подходят под тип параметра функции.
не совсем удачный пример поскольку достаточно хороший вывод типов мог бы сделать то же самое и при номинальной системе типов, я полагаю 🤔
вообще-то классы придумали чтобы работали нормальные математические операции, например:
distance = point1 - point2;
point3 = point1 + point2;
Но это видимо слишком страшная тайна чтобы про нее вспоминать.
Нет. Классы придумали не для этого. Классы в программировании это изначально, в момент придумывания, вообще про обмен сообщениями между машинами состояний. Ближе всего к “изначальному ООП” - Эрланг.
Нет, для "нормальных математических операций" достаточно было сделать возможность объявлять функции с именами "+","-", ... и принимать структуры в качестве аргументов. Нет никакой причины, по которой операция умножения вектора на скаляр должна быть членом класса вектора.
для "нормальных математических операций" достаточно было сделать возможность объявлять функции с именами "+","-", ... и принимать структуры в качестве аргументов.
ну расскажите тогда как компилятор должен искать функции с именами "+","-", которые принимают структуры в качестве аргументов. Это первое.
Еще попробуйте выбрать при компиляции правильный способ передачи параметров в функцию на уровне машинных инструкций. Сколько способов передачи параметров в функцию на уровне машинных инструкций вы знаете?
Действительно, если куда надо довезет извозчик (компилятор-интерпретатор) - нет никаких причин знать-изучать-понимать географию (методы компиляции)!
ну расскажите тогда как компилятор должен искать функции с именами "+","-", которые принимают структуры в качестве аргументов. Это первое.
Вообще не проблема. Когда компилятор встречает объявление функции, он запоминает в своей глобальной таблице не только её символ-имя, но и типы аргументов. Когда встречается вызов, то поиск и выбор вызываемого кандидата происходят по имени и типам значений аргументов. Это ничем не отличается от перегрузки функции, имя которой не "+", какая разница, какие буквы входят в имя? Так работает перегрузка функций в C++, например. Да что далеко ходить, даже автор этой статьи осилил перегрузку, но у него структуры анонимны, а их тип составляется из типов полей, сути это не меняет. Заменить инфиксную запись a+b на синтаксис вызова +(a,b) элементарно на уровне парсера.
Еще попробуйте выбрать при компиляции правильный способ передачи параметров в функцию на уровне машинных инструкций. Сколько способов передачи параметров в функцию на уровне машинных инструкций вы знаете?
Вы задаёте очень простые вопросы таким важным тоном, будто спрашиваете что-то безумно сложное. Ответ простой: зависит от того, что есть в языке. Если язык поддерживает указатели, и в качестве типа аргумента указан указатель на структуру, то передать его хоть через стек, хоть в регистре, разница невелика. В Java/C# нет указателей, и структуры аллоцируются всегда в куче, а переменные/поля/аргументы хранят ссылки. Если структура передаётся не через указатель, и язык реализует семантику копирования, как C, то в кадре стека вызванной функции аллоцируется место под структуру и содержимое копируется туда.
Это не имеет никакого отношения к классам и к перегрузке функций, базовые вещи. Не понял, к чему был этот вопрос.
Действительно, если куда надо довезет извозчик (компилятор-интерпретатор) - нет никаких причин знать-изучать-понимать географию (методы компиляции)!
Вы не дождались ответа на свои вопросы, и заранее меня тупым пытаетесь выставить?
В целом верно! Но проблема в том что вам как раз наплевать на эти детали, для вас это ни на что не влияет, вы отрицаете что это имеет отношение к концепции классов, поэтому, мне кажется, я в своей оценке не ошибся, все таки.
Не знаю может вас все таки заставит задуматься такой факт: компилятор не только должен найти функцию которая подходит по типам для опрерации, но и проверить разрешил ли прораммист использовать эту функцию для этой операции в контексте перечисленных типов.
По поводу :
В Java/C# нет указателей, и структуры аллоцируются всегда в куче, а переменные/поля/аргументы хранят ссылки.
любая переменная (поле, даже функция) в любом языке это просто адрес в памяти, читай указатель для компилятора. После компиляции имена сохраняются только если они нужны для отладки, только для человека, машине они не нужны, машина работает исключительно с адресами, с указателями.
Это настолько очевидные и элементарные вещи, что даже не обсуждаются
После компиляции имена сохраняются только если они нужны для отладки, только для человека, машине они не нужны, машина работает исключительно с адресами, с указателями.
это неверно или слишком упрощая. типы сохраняются и на этапе ir-кода и на этапе линковки
а еще можно вспомнить про rtti и интроспекцию
или слишком упрощая. типы сохраняются и на этапе ir-кода и на этапе линковки
а еще можно вспомнить про rtti и интроспекцию
когда придумали классы ничего этого не было! Даже JAVA не было! Поэтому, если мы рассуждаем о том: А нужны ли нам классы, не надо усложнять, как мне кажется, надо опираться на фундаментальные- базовые задачи программирования.
а как оно противоречит? С удовольствием признаю свою ошибку, но пока я ее не вижу.
вообще-то классы придумали чтобы работали нормальные математические операции
Поэтому, если мы рассуждаем о том: А нужны ли нам классы, не надо усложнять, как мне кажется, надо опираться на фундаментальные- базовые задачи программирования
возможно, нечеткая формулировка или "съехала" тема применения классов
Не знаю может вас все таки заставит задуматься такой факт: компилятор не только должен найти функцию которая подходит по типам для опрерации, но и проверить разрешил ли прораммист использовать эту функцию для этой операции в контексте перечисленных типов.
Если есть функция, которая принимает на вход в точности указанные типы, то какое ещё разрешение нужно, чтобы её вызывать с указанными типами? Что вообще означает это "разрешил использовать для операции в контексте типов"? Можно пример какой-нибудь?
Я продолжаю утверждать, что для выражения математических операций не нужны классы, достаточно механизма перегрузки функций, возможности называть функции математическими символами и синтаксического сахара для инфиксных вызовов. Haskell не даст соврать.
Что вообще означает это “разрешил использовать для операции в контексте типов”?
Ну например, private метод ?
так да. private, protected это плюсовые добавления, в чистом С были(есть) модули там static работает как private в рамках модуля.
Вообще удивительно насколько люди бывают самоуверенные и ограниченные. С заявлениями типа: "Классы не нужны!". То есть те кто придумали классы сделали глупость, мы лучше знаем! Это прискорбно наблюдать.
И указатель на класс в С++ передается в функцию специальным образом, это хоть и мизерное повышение эффективности, но тем не менее! Кроме того на этом разные техники программирования строятся, это придумали люди которые решали задачи на уровне машинных инструкций, задачи о которых теперь очень мало кто знает, это в гугле не прочитаешь! А если и прочитаешь - то не поймешь без определенной базы.
Я удалил классы, но оставил объекты с методами