Pull to refresh

Comments 103

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

Вот только джава уже на протяжении двух десятилетий сохраняет обратную совместимость, и похоже никто не собирается менять эти чудесные традиции.
Тебе не понять романтики одинокого малайского язычника в толпе мусульман, который смотрит на вулканы и мечтает о новых Кракатау. «Сила взрыва Кракатау оценивается в 100—200 мегатонн тротила, что более чем в 10 тысяч раз превышает силу взрыва, уничтожившего Хиросиму». Индонезия. С моей точки зрения, сложность и запутанность кода растёт быстрее производительности компьютеров, и, если так дело пойдёт, то, лет через 50 будут не роботы по улицам ходить, а священнослужители с кадилом перезагружать постоянно зависающие девайсы. Профессия такая появится наподобии DevOps — «девпоп».
Не понимаю, народу не нравится вулканизм, черти, ладан или философские обсуждения программирования считаете бессмысенными? Сказал же: Ява ни при чём. Мне казалось, это и так понятно было.
Имхо, проблема в инертности мышления и связанном с этим придумывании несуществующих правил. Это не первая статья, которую несправедливо заминусовали прямо в карму, не читая. Люди так привыкли, что на хабре пишут исключительно экспертные статьи вида «я эксперт, вам нужно делать так и так», что совершенно любой другой жанр рассматривается как нарушение устоев.

То же касается и комментариев: сложилась практика (по-моему, весьма адская) плюсовать и минусовать комментарии исходя из экспертности. То есть, если человек пишет в ответ «а мне нравится по-другому», и тебе нравится как-то иначе, нужно сжечь собеседника минусами в комментарий. Это опять же неявно подразумевает, что минусующий считает, что его собеседник знает правду и намеренно распространяет ложь. Люди слишком привыкли, что существует не более одного формата общения — того, в котором целый тред пытается что-то доказать друг другу. Отсюда например, часто встречающийся паттерн «лесенки минусов» (все комментарии в ветке — перепалка двух человек, которые минусуют друг друга) или «лесенки чётных минусов» (та же перепалка, но сообщество решило, что виноват чётный оправдывающийся, и его минусят сразу десятками, сотнями). Формат обсуждения, когда люди просто пытаются в ходе обсуждения найти какое-то решение в это никак не помещается.

Что с этим делать — непонятно. Наверное, вначале зарабатывать некий капитал из плюсов и потом сливать его на нестандартных статьях, пытаясь выработать привычку читать что-то кроме экспертных статей.
Вы слишком глубоко копаете. Люди минусуют статьи и комментарии, в которых видно, что человек пишет о предмете, в котором он ничего не понимает.

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

Ну ведь не будете же вы выкладывать на Хабр статью о том, как вы написали свою первую сортировку пузырьком? Да даже и merge sort in place (хотя это сложный достаточно алгоритм) — тоже не будете. А обсуждаемая статья — это примерно этот уровень и комментарии это только подтверждают.

"Ничего не понимает" — это вещь относительная. То, что пишет мидл — это какая-то постыдная чепуха для сеньора. И наоборот, то что пишет джун — откровение для новичка. Для вашей прабабушки вы всегда будете "компьютерным гением". Требование всегда "чего-то понимать" означает, что вы наложили на автора поста или комментария обязанность быть экспертом в обсуждаемой теме, экспертнее чем большинство на Хабре. Вроде как если бы уроки математики в школе мог вести только академик.


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


Когда-то давно мне казалось, что если я не понимаю код, это значит — я тупой и нужно учиться. Сейчас пришло понимание, что если я не понимаю код, то скорей всего — код очень, очень плох :-)


Дискуссия между обычными людьми (не экспертами) часто выглядит так: люди говорят в рамках своих крайне лимитированных знаний и в конце концов, дополняя дыры в понимании друг друга, приходят к каким-то выводам. Результат разговора не обязательно будет корректный, но зато все получат удовольствие и узнают что-то новое. Это обычный разговор о истории в курилке, например: два коллеги-программиста обсуждают какую-то войну в V веке, о которой помнят лишь по обрывкам институтских лекций и в ходе этого диалога упражняются, например, в логике. Никто из адекватных людей не бьёт друг другу лицо в курилке и не бьётся в конвульсиях от того, что кто-то из собеседников не прав в отношении войны в V веке!


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


Запретить все разговоры в курилке? Поставить там крупнокалиберный пулемёт и разрывать на куски мяса всех, кто сказал нечто, противоречащее школьному учебнику (то есть, вообще всех)? Не хотел бы жить в таком мире.


> Ну ведь не будете же вы выкладывать на Хабр статью о том, как вы написали свою первую сортировку пузырьком?


Ещё как буду! Помойму там есть много чего обсудить и спросить у экспертов, что и как правильно писать.

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

Требование всегда «чего-то понимать» означает, что вы наложили на автора поста или комментария обязанность быть экспертом в обсуждаемой теме, экспертнее чем большинство на Хабре.
Разумеется! А иначе какой смысл писать статью? О предмете, в котором вы не разбираетесь?

Вроде как если бы уроки математики в школе мог вести только академик.
Нет, это как если бы уроки математики в школе вёл физрук. Для комедии — сойдёт, а для Хабра — нет.

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

Улучшения иногда делают те, кто не разобрался.
Иногда — да. Но очень-очень редко. В большинстве случаев улучшения как раз делаются теми, кто разобрался — и понял что и откуда можно выкинуть.

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

Одно дело — когда вы обсуждаете что-то со свомими 5-6-10 лучшими друзьями, совсем другое — когда вы вовлекаете в это тысячи людей, и совсем третьей — когда вы о чём-то говорите с тысячами экспертов на конференции.

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

> Разумеется! А иначе какой смысл писать статью?


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


Больше скажу, признанные эксперты так делают доклады на профильных конференциях. Например, пару дней назад на FOSDEM (крупнейшая в мире free open source конференция), на джава-треке, Christine H Flood (наверное, не надо ее представлять? это дизайнер языков включая Java и LISP с десятками лет опыта, работающая сейчас в RedHat) рассказала просто о проекте, которым она занимается по фану. Нет, она не продумала хорошо то, что делает, и о чем собирается говорить, и половина ответов на вопросы звучала как "я делегирую эту проблему утилите CRIU, она может быть это делает… а может быть и нет, спросите у её автора, он тут есть на конференции". От этого у многих в зале конкретно бомбануло, они ожидали от человека такого уровня чего-то более другого, более масштабного, каких-то громких заявлений от имени RedHat — и ничего этого не было. А я наоборот радовался — вся жизнь прошла, а девушка так и не потеряла способности относиться к происходящему легко, как к игре, как к искусству, без всей этой фигни вида "я эксперт с N-цать летним стажем, сейчас расскажу, что вам делать". Она пришла на сцену просто чтобы поджечь интересную дискуссию, и своей цели добилась!


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

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


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

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

Она пришла на сцену просто чтобы поджечь интересную дискуссию, и своей цели добилась!
Это иногда работает с «живой» аудиторией (хотя почти всегда это — результат недостатка времени на подготовку, так что приходится «вытягивать» провальное, по сути, выступление, за счёт харизмы). И почти никогда не работает в интернете.

Хинт: есть специальные Q&A сайты, где подразумевается, что ответы даются не просто из желания почесать языком, а технически грамотные советы. Например, Тостер или StackOverflow. На SO за попытку флуда в комментариях придут кровавые модераторы и турецкими саблями разрисуют хулигана под хохлому. Там это официальное правило, что "Дума SO — не место для дискуссий", там всё как вы хотите. На SO никогда не случился бы диалог, который мы ведём сейчас.

Есть, да. Там переход на другие темы не приветствуется. Требуют создавать новую тему и там обсуждать.

На Хабре — с этим особо не борются. Но тратить время и силы на объяснение того, что человек и так, вроде как, должен знать (если он собрался рассуждать на определённые темы) редко кто будет.
Какая разница, какой информацией вы владеете или не владеете, если вы всё равно не умеете с ней работать. Даже, если, предположим, лично вы и умеете это в другой ситуации, то общей удручающей картины это не отменяет. Как вообще люди умудряются со всем этим психологическо-социальным грузом создавать сложные системы, которые в принципе, по факту самой своей сложности, склонны порождать новую и ситуативную семантику? Вот это и есть тот самый главный баг.
Кстати, khim, вы не ответили ниже по существу и не одному мне, там какие-то properties. Вообще, работа мозга это очень и очень занятная штука, а ещё занятней — работа системы мозгов да ещё и в связке с нервными системами. Не даром устройство нейрона до сих пор никому не известно. В средние века в первом классе школьники своё образование начинали с «тривиума» — логика, риторика, грамматика. У нас первые два куда-то перенеслись настолько на очень никогда потом, что умереть со смеху. Представляете, вы на энном курсе института вскользь прошли бы арифметику. Аккурат после матанализа. Нормально? Вот и мне смешно.
Весь этот мерзкий тривиум на самом деле пошёл от чудака Сократа, который очень хорошо понимал то, что ничего не понимает. Такое не каждому дано. Т.е., «ничего не понимает» это комплимент и ещё какой. После Сократа жила старушка Грейс Хоппер, автор Кобола, одной из целей которой было максимально приблизить конструкции Кобола английскому языку, и ещё в 2006 году Кобол считался языком программирования, на котором было написано больше всего строк кода — из Википедии. Двуногим же без перьев типа меня обычно и приходится юзать всё такое гениальное, а вдруг и правда гениальное — я же не спорю. JavaScript был там кем-то из нетскейп что ли написан за десять дней чтобы только не визжал бейсик. И опять чувак попал в струю. Очевидно, я так не умею, но мне приходится с этим иметь дело.
Разговор просто перешёл в состояние, когда стало ясно, что ничего конструктивного из него не родится. И сама-то статья состояла из какого-то малоосмысленного «потока сознания», а когда выяснилось, что вместо обсуждения плюсов и минусов разных решений описанных вами проблем в разных языках нужно объяснять «а как это вообще бывает»…

P.S. По поводу языков, которые «оказались в нужное время в нужном месте»… JavaScript это ещё не «ужас летящий на крыльях ночи». Настоящий ужас — это Perl и PHP. По крайней мере JavaScript был рождён из очень пристойного языка, пусть и сильно изуродованного. Perl и PHP — были рождены примерно тем методом, про который вы говорите… и многие году люди занимаются тем, что пытаются их «выпрямить»… с не слишком большим успехом, прямо скажем.
В принципе, работа программиста и состоит в том, чтобы что-то чему-то объяснять, особенно сложно это делать тупому компилятору. Таки вы этого умения почему-то не демонстрируете, всё остальное — лирика. Докажите мне, что я тупее компилятора. У вас это не получится никогда, зато, я знаю ваши проблемы как свои пять пальцев и они тоже социально-лингвистические, если хотите.
Брендан утверждает, что идея была именно Scheme внедрить — но начальству не понравились скобочки.
Что с этим делать — непонятно.

Отлично понятно.


  1. Никакое сообщество не может саморегулироваться. Это аксиома. Нужны хоть какие-то модераторы.
  2. За минус в карму нужно ставить минус в карму самому минусующему. Это чуть-чуть сбалансирует "потенциал саморегулирования" у людей с нарушением зрения, видящим либо чёрное, либо белое и ни одного оттенка серого.
Да Ява тут вообще ни при чём, просто как модель какая-то, типа Си для той же Явы. В Яве мне нравится типизация, как-то не доводилось встречать такую же удобную в других языках.
Для «protected», «default» и «public» полей автогенерация сеттеров и геттеров компилятором, без автогенерации видимых сорцов, но с возможностью их перегрузки.

Было бы круто. А если бы делфишный property осилили так и вообще молодцы были бы.
Глянул делфишные проперти — чего-то там много букв и надо думать, а думать на жаре больно. Кто его знает, может быть вы и правы, не могли бы вы подробнее описать в чём их суть и что она привносит?
Возможноcть не писать бесконечные геттеры и сеттеры в Java-стиле. Они же есть и в C#.

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

В Java вам на каждую переменную нужно создавать две функции, чтобы было «по феншую». Геттер и сеттер. Даже если эти функции ничего не делают с переменной, просто кладут её в поле класса и вынивают из него.

Дальше — бесконечные автогенераторы и прочая мура.

В C# (и, как прародителя, но не изобретатели, этой фичи, в Delphi) вы просто объявляете переменную public — и… всё. А если позже оказывается, что всё-таки нужно что-то делать при изменения поля — то вместо этого поля заводится property.

Это лучше вашего предложения более естественным синтаксисом. Ну можно window.width = 125; выглядит понятнее, чем window.setWidth(125);
У меня была мысль, что поля можно модифицировать и читать напрямую из родного класса, благо классы должны получаться малюсенькими, т.е. синтаксис присваивания уже занят и, если его использовать, то уже надо что-то придумывать для родного сеттера и геттера. Кроме того, у мне была мысль, что все поля private, т.е. в наследниках доступны только сеттеры и геттеры, а пишутся и читаются только свои. Поля с именами, идентичными таким же в предке, я бы не давал делать, это в Яве чего-то мешающая ерунда какая-то.

С присваиванием все просто — доступ к полю приоритетнее, и тогда ничего из уже написанного не сломается. И даже хрен с ним уж с присваиванием — пусть так и оставят setXXX(). Главная задача пропертей — это добавить сахорочку и уменьшить бойлерплейт на этапе определения объекта. Потому как более чем в 99% случаем все ваши геттеры-сеттеры — это тупо автогенерация доступа к полю. Для всех остальных случаев оставить обычный фолбэк в геттеры-сеттеры.


Сейчас для создания одного! свойства в Java приходится писать его имя 7-12 раз, его тип 3 раза, 2-3 раза повторяющийся джавадок и в общей сложности используя 19+ слов. Это есть настоящий идиотизм.


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

Дело в том, что тут вообще не Ява обсуждается, даже не типа того, что Ява это далеко не Си, а ещё дальше. По всей видимости, абсолютное большинство не понимает сути написанного, но статья получилась случайно и я был очень ограничен во времени, когда писал, хотя, с другой стороны, даю гарантию, что продумывал эту парадигму много лет и могу ответить-скорректироваться по существу.
В парадигме и есть всё как бы интерфейс. Точнее, типизированый объект, для которого список интерфейсов отслеживается виртуальной машиной, механизм описан в пункте 4. В парадигме вообще нет ничего кроме единственной конструкции со скобками. Ни классов, ни методов, ни явных интерфейсов, но объекты типизированы и морды у них вполне абстрактные. Плюс, Си образная внутренность метода, в которой уже есть и поля и прямое присваивание и прямое считывание того, что доступно для данных операций внутри этого маленького объекта. Просто, в этот метод затесались другие методы как в Паскале, ну и не local переменные можно конечно вынести в шапку. «throws» просто идентифицирует тип вызывающего метода чтобы в случае exception срезать стек даже без создания нового объекта и без выделения памяти (в случае outOfMemory смешно, да?), а в методе-обработчике этого exception об этом throw и срезаниии узнают только косвенно из аргументов, дальше — нормальное возвращение в вызывающий метод своего «класса». Этот обработчк — тоже просто метод, который принимает какие-то аргументы и сообщения.
Вот выдержка из текста обсуждения по поводу пропертей:
«Представляете, как выглядит текст такой программы? Он совершенно не похож на то, что в Delphi и Java и в C++. Он — что-то типа Лиспа, очень малое соотношение числа строчек кода к числу типов. Фактически, нижнему по иерархии классу чаще всего придётся работать с единственным полем. Поскольку, класс-метод, который уже что-то делает вразумительное, скорее всего будет внешним. В таком случае, разве, одних внутренних же методов не достаточно (без своих полей вовсе)? ».
«Больше всего памяти занимают массивы. Логика это такая вещь, которая памяти не потребляет и чем больше будет ООП, связанного с логически важными переменными, тем лучше. Это основное.
Ньюанс, мало влияющий на основное, но, всё же: в современных проектах на Java и так все поля спрятаны private и, хотя не все из них имеют геттеры и сеттеры, но, очень часто — большинство. Неявную же автогенерацию set/get легко оптимизировать простым флагом для случая отсутствия явно объявленного метода. Доступ виртуальной машины к полям класса и так не прост, это вам не доступ по адресу.»
Пункт 2 кому-то был непонятен. Вместо статичных классов и полей — объекты по умолчанию. Внутренние объекты по умолчанию, таким образом, будут как бы статичными только для себя, по отношению к своему внешнему классу, т.е., если внешний класс сам статичный и так до первого предка, то — статичные абсолютно. Т.е., при запуске создаются полные статичные структуры и больше ничего, которые можно клонировать либо оператором new, либо вызывать как обычные методы, если ссылки на объект не нужны. Все не-local поля, при этом, разумеется, создавать тоже как local, считывая значения по умолчанию, ссылки-то не нужны. Таким образом, «конструктор» выделяет память для всех своих переменных, что ежу понятно, а когда дёргает поля внешнего класса — не выделяет память, что тоже ежу понятно. Для того он их дёргает, чтобы быть в связке с состоянием внешнего «метода-класса». Таким образом, из внешнего класса можно считать дефолтные значения любого внутреннего класса — они получаются как бы статичными для самих себя (см начало).

По поводу NPE: «аbstract» модификатор полю означает NPE при попытке считать дефолтное и невозможность создать экземпляр содержащего класса без явного задания этого поля. При попытке считать ссылки на явно не инициализированные объекты — доступ к дефолтному объекту этого типа, а все примитивы обязаны либо явно инициализироваться, либо объявляться abstract.
Это лучше вашего предложения более естественным синтаксисом. Ну можно window.width = 125; выглядит понятнее, чем window.setWidth(125);

  public void setAnything(Object mayBeNull) throws IllegalArgumentException {
    if (mayBeNull == null) throw new IllegalArgumentException("Everything you can imagine is real (c) Pablo Picasso");
    this.canNotBeNull = mayBeNull;
  }
а что можно написать вместо этого, если нужно проверить на nil?
В парадигме «window.width = 125», на null придется проверять вне модифицирующего метода. Инкапсуляции не станет. И наступит хаос…

PS: причем null это еще не самое страшное. Вот классический пример из википедии
public void setHours(int hours) {
         if ((hours >= 0) && (hours < 24))
             this.hours = hours;
     }
то есть ексепшена в будущем от использования null не будет, зато влегкую может быть нарушена бизнес логика при obj.hours = 25 или obj.hours = -12
Хаос наступает, если большинство указателей могут быть указателями на null.

В C++ для этого есть ссылки. В Kotlin — nullable классы. А в Java — да, хаос. Но не из-за properties, а из-за того, что в любую дырку можно null засунуть, а потом пыль глотать exception'ы ловить.
я чуть позже привел пример с примитивами
Ваш «пример с примитивами» как раз легко обрабатывается с помощью properties. Они как раз для подобных случаев и нужны.
Мне нравится однозначность в языках, это когда то, что ты видишь, то и делается. А не как в Си++ в котором можно перегрузить вообще всё так что не поймёшь на каком языке написано. Это перебор. Когда сеттер это сеттер, то после любого копипаста никаких скрытых синтаксических изменений не будет. Что если я из внешнего класса скопирую во внутренний это присвоение, которое раньше было сеттером, и наоборот. Это очень неудобно с моей точки зрения.
Когда сеттер это сеттер, то после любого копипаста никаких скрытых синтаксических изменений не будет.
Вот только потом возникает желание эти сеттеры наавтогенерировать и получаем «те же яйца, вид сбоку» — только ещё и хуже читабельность.

Что если я из внешнего класса скопирую во внутренний это присвоение, которое раньше было сеттером, и наоборот.
А это тут вообще причём? properties всегда действуют.
В методе который дёргается снаружи можно проверять всё что угодно и делать всё что угодно вплоть до логирования. Внутри класса, если класс маленький, нужен прямой доступ к переменной, а если класс большой, то лучше запасаться консервами в ожидании ядерной зимы. Не так?
Если вы используете property то у вас вызывается один и тот же код всегда.
Т.е, как, всё-таки, это может уживаться с работой изнутри родного класса? Тут по существу вложенные методы просто, очень маленькие классы. Проперти у меня из памяти конечно выветрились, но большие такие классы Делфи я помню, поскольку даже Паскаль не мой первый язык. Там обычно, да и в Яве сейчас очень и очень часто, такая большая сарделька которая понятно как образовалась — люди ночами не спали домой убежать мечтали а их тимлид не пускал. В таких условиях польза от пропертей очевидна, я в таких классах даже локальные копии всегда в методе устраиваю, чтобы потом слить всё что надо, а тут я попытался уйти от этой истории. Тут куча мелких вложенных классов как в Лиспе что ли.
В каком будущем, это о чём? В Яве сейчас есть. Каким образом стало «obj.hours = 25 или obj.hours = -12»? И каким образом бизнес логика, явно заданная вот этим вот сеттером, может быть нарушена, если null будет иметь дефолтное значение? С моей точки зрения, если часы обнулятся по дефолту, то это лучше всего остального. Так ведут себя любые часы.
По поводу первого — так я и не спорю, мне сеттеры и проверки на null очень даже нравятся когда такие проверки нужны.
Ну если не нравится пример с часами, замените их на myBankDepositAmount и присвойте ему -USD100500,00 :)
А о чём вообще речь?
О том, что кто-то пытается обсуждать проблему properties не имея ни малейшего представления о том, как они работают…
Я не очень понимаю профита от модели property в дельфи. Например, как я сообщу внешнему миру, что полученные данные не валидны? Не молча проигнорирую, а именно «кину наверх» ошибку с внятным описанием/типом?
Так же, как и в случае с сеттерами в Java. Exceptions никто не отменял.
И как по вашему будет визуально выглядеть такой код? Я честно не понимаю, почему try { obj.amount = 100 } catch () {} более читабельный нежели чем try { obj.setAmount(100) } catch () {}
Второе: если я правильно помню, в дельфи остались те же самые геттеры и сеттеры. То есть мы можем реализовать изменение значения поля обьекта двумя путями, верно? Вы считаете это повышает читабельность кода?
То есть мы можем реализовать изменение значения поля обьекта двумя путями, верно?
Неверно.

Вы считаете это повышает читабельность кода?
Да. Именно потому что вы не можете присвоить значение полю иначе, как через property (даже методами внутри класса) вы можете быть уверены в том, что инварианты, связанные с этими полями всегда сохраняются.
Всё же в Delphi property присущи только классу, а не подпрограммам. Мне же хотелось как раз уйти от такой объектной модели. Property наверное очень полезны, но вот как будто абсолютно очевидно, что внутри очень маленьких классов они абсолютно излишни. Кто знает, кто знает
Если вы хотите узнать как использовать проперти — ну загляните, наконец, в учебник.

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

Copy-Paste смысл ваших опереаций не поменяет. И проверки при переносе куда ниоткуда не возникнут.
По вашей ссылке написано ровно то, о чём я написал сообщением назад. В остальном, причём здесь синтаксис Делфи? Как это будет с точки зрения процессора, виртуальной машины? От простой мысли, что, скорее всего, в изложенной мною модели это избыточно и неудобно, вы прячетесь как чёрт от ладана, хотя уже можно было формализовать всё, что угодно. Чувак за время нашего с вами общения уже написал двадцатую часть javaScript, а мы топчемся на месте. Так работать нельзя. Не хочу я работать так. Ни с кем, и хорошо, что я с вами ещё не работаю, а жаль, что придётся — если не с вами, то с кем-то таким же, придётся вас гипнозом менять. Вы поддаётесь гипнозу? Что на вас действует? Жёлтые штаны?
UFO just landed and posted this here
Для начала вам придётся увязать нечто из этих лекций с тем, что тут. Понимаете, что мне не нравится и вашем посте также — ссылки на какую-то сакральную информацию. Информацию нужно уметь презентовать, увзязывая с интересом слушателей. Иначе это баг мышления.
UFO just landed and posted this here
Да, всё так, спасибо
Все же я плохо помню парадигму пропертей в дельфи, но если она точно подразумевает невозможность создания дублирующих явных геттеров и сеттеров, а также возможность указать только read или только write, плюс можно будет красиво и читабельно оформить в сигнатуре несколько эксшепшенов с разными «типами», то почему бы и нет.
Но все же хотелось бы взглянуть на то, как это могло бы выглядеть на примере. Что то мне подсказывает, что простых красивых конструкций мы не получим.
 TSomeClass=class(Tobject)
    FStr: string;
    FAStr: array of string;
    procedure SetStr(A: string);
    function GetStrByIndex(I: Integer): string;
  public
    PStr: string;
    property AStr[I: integer]: string read GetStrByIndex;
    property Str: string read FStr write SetStr;
  end;

{ TSomeClass }

function TSomeClass.GetStrByIndex(I: Integer): string;
begin
  Result:=FAStr[I];
end;

procedure TSomeClass.SetStr(A: string);
begin
  Self.FStr:=A;
end;

Вот простой пример использования property. Что вам конкретно не понятно или смущает в этом механизме?
На самом деле, так получилось, что я property использовал 15-20 лет назад в Delphi и мне сразу показалось, что в изложенной мной модели это обыгрывается всё тем же внутренним методом, поскольку внутри этого маленького «метода-класса» все филды — private. Что может быть проще? Так или не так?
Поскольку, за основу я взял труп Java и final не убирал, то, в итоге, объявить final «класс-поле» и филд внутри, примитивный или опять ссылку — не проблема.
Перегружать синтаксис присваивания мне кажется неуместным тут. Это всё же методы, а внутри них — всё же настоящие присваивания своим филдам.
Просто, ваше замечание по существу было одно и первых и в такой категоричной форме, что хотелось услышать расширенный комментарий в контексте обсуждаемой темы, а не в контексте Java или Delphi. Потому что property есть не только в Delphi, да и Delphi за много лет мог поменяться, и вообще, мало ли кто и что может придумать.
Для «protected», «default» и «public» полей автогенерация сеттеров и геттеров компилятором, без автогенерации видимых сорцов, но с возможностью их перегрузки.
Вы попросили возможность писать напрямую в переменные + возможность перегрузки автосгенерированных методов и на этом все. Но property это не просто перегрузка метода присвоения, это и возможность ограничить только чтение или только запись в переменную на уровне компилятора, возможность забрать/записать переменную из массива/списка по индексу.
Полный список возможностей в примере
1.Property Name : Type read Getter|nodefault;
2.Property Name : Type write Setter;
3.Property Name : Type read Getter write Setter;
4.Property Name : Type Index Constant read Getter {default : Constant|nodefault;} {stored : Boolean};
5.Property Name : Type Index Constant write Setter {default : Constant|nodefault;} {stored : Boolean};
6.Property Name : Type Index Constant read Getter write Setter {default : Constant|nodefault;} {stored : Boolean};
7.Property Name[Index : IndexType] : BaseType read Getter {default;}
8.Property Name[Index : IndexType] : BaseType write Setter; {default;}
9.Property Name[Index : IndexType] : BaseType read Getter write Setter; {default;}
10.Property Name : Type read Getter implements Interfaces...;
11.Property Name;                  // Повторно объявленное свойство базового класса
12.Property Name : Type;           // Только Dispinterface
13.Property Name : Type readonly;  // Только Dispinterface
14.Property Name : Type writeonly; // Только Dispinterface


Тут ньюанс в пункте 8 статьи. Представляете, как выглядит текст такой программы? Он совершенно не похож на то, что в Delphi и Java и в C++. Он — что-то типа Лиспа, очень малое соотношение числа строчек кода к числу типов. Фактически, нижнему по иерархии классу чаще всего придётся работать с единственным полем. Поскольку, класс-метод, который уже что-то делает вразумительное, скорее всего будет внешним. В таком случае, разве, одних внутренних же методов не достаточно (без своих полей вовсе)?
Может быть, есть смысл как-то дополнительно информировать пользователей класса о чём-то?
Фактически, нижнему по иерархии классу чаще всего придётся работать с единственным полем.
Цель какая? Сделать из Xeon'а Arduino? Вы не забываете, что одно обращение в память занимает столько же времени, сколько 300-400 арифметических операций (нет, тут нет ошибки в нулях). Ну и про потребление памяти на вот-это-вот-всё тоже не стоит забывать. А то Electron получится.
Категорически не согласен. Больше всего памяти занимают массивы. «Property» (в общем смысле) это такая вещь, которая памяти не потребляет и чем больше будет ООП, связанного с логически важными переменными, тем лучше.
Обсуждение возникло из предпосылки «Возможноcть не писать бесконечные геттеры и сеттеры в Java-стиле.»

«Или я чего то не понимаю, или одно из двух», но в приведенном выше примере количество текста не меньше, нежели чем в классическом варианте getA(), setA(String A)

И как будут выглядеть проверки для сеттера и пробрасывание наверх 1...n эксепшенов?
а. У меня обсуждение возникло из пункта 3 статьи выше. В этом пункте самые примитивные сеттеры и геттеры я уже убрал автоматизацией с возможностью перегрузки для чего-то более сложного, тем самым, бесконечными они быть перестали. Причём, автоматизировано было даже NPE, пунктом 2. Потому что достаточно обернуть поле одноимённым «классом» и добавить полю модификатор abstract. Ни присвоить абстрактному полю «статичное, по умолчанию» значение (пункт 2 и пункт 8.3), ни создать объект для присвоения без явного объявления значения — не получится. Попытка обращения к этому abstract полю по ссылке на содержащий его дефолтный объект — тоже NPE, этого я не убирал.
б. Наверное, это может быть удобно для больших Ява классов тем, что дисциплинирует и не даёт возможность обходить сеттеры и геттеры, но я не хочу тут разбираться с другими парадигмами без связи с той парадигмой, которая изложена в статье.
в. Проверки абcолютно так же, как в Java, если понадобятся. Ексепшены описаны в пункте 9. Когда я статью писал, я старался думать как раз ну уровне процессора и виртуальной машины, а не просто поток мыслей выкладывал, как кому-то могло показаться.
В этом пункте самые примитивные сеттеры и геттеры я уже убрал автоматизацией с возможностью перегрузки для чего-то более сложного, тем самым, бесконечными они быть перестали.
От того, что вы их не видите — они бесконечными быть не перестали. Они по-прежнему отжирают память и время исполнения (да, конечно, JIT-компилятор может с ними бороться, но во время запуска и остановки приложений — а это больное место Java вообще — всё это вылазит).

Что вы там убрали пунктом 2 — я, если честно, не понимаю, по-моему вы просто заменили хорошо видимую проблему, на ошибку в логике программы — что добром не кончается.

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

Если вам нужен язык в 10 раз более прожорливый по памяти и в 100-1000 раз медленнее — то таких уже есть. Много. Разных.
Вы повторились, не ответив на моё сообщение «Категорически не согласен». Немного перефразирую. Больше всего памяти занимают массивы. Логика это такая вещь, которая памяти не потребляет и чем больше будет ООП, связанного с логически важными переменными, тем лучше. Это основное.
Ньюанс, мало влияющий на основное, но, всё же: в современных проектах на Java и так все поля спрятаны private и, хотя не все из них имеют геттеры и сеттеры, но, очень часто — большинство. Неявную же автогенерацию set/get легко оптимизировать простым флагом для случая отсутствия явно объявленного метода. Доступ виртуальной машины к полям класса и так не прост, это вам не доступ по адресу.
Т.е., ваше утверждение о прожорливости и медленности такой модели пока не доказано.
Кроме того, в пункте 11 предложено читать массивы напрямую из памяти без проверок. Вы представляете, какой прирост производительности с этим может быть связан в проектах, манипулирующих массивами данных, ещё и во всяких циклах?
«Не доказано» это мягко сказано. Вообще, не понятно, как человек, претендующий на опыт в программировании, может делать такие порядковые ошибки в оценках («10, 100, 1000»), не понимать, на что в проектах уходит память со времён malloc. Либо он сильно ангажирован и работа с ним невозможна, либо он не имеет достаточного опыта в разработке.
Опять же, риторика. Вы весьма невежливо сообщили, что не понимаете нечто в пункте 2, намекнув на некую логическую ошибку. Логическая ошибка это противоречие, на которое нужно указать явно, иначе непонятно, что конкретно нужно уточнять. Указать на противоречие можно помощью цитирования предположительно противоречащих друг другу утверждений собеседника — это самый простой способ. Иначе, утверждение «всё, что там написано — логическая ошибка» будет называется ложным.
«Или я чего то не понимаю, или одно из двух», но в приведенном выше примере количество текста не меньше, нежели чем в классическом варианте getA(), setA(String A)
Все приведённые примеры описывают редкий случай, когда геттеры/сеттеры и/или property реально нужны.

В случае если они не нужны — вы заводите public поле — и всё. Вот совсем всё.

Потребуется обработка или контроль доступа — заведёте property. А поскольку любой, заведённый вами property делает нетривиальную обработку — то «автогенерация сеттеров и геттеров компилятором» ну нужна. От слова совсем.

Без property вы этого сделать не можете, так как код из «window.width = 125» в «window.setWidth(125)» автоматически не превратится.
Обращение к паблик полям убивает полиморфизм на корню. Поля в изложенной парадигме private автоматом без объявлений, и в стандарте Java не случайно тоже private.
Контроль доступа потенциальный или явный нужен всегда для внешних обращений и никогда для внутренних, в случае методов. Контроль доступа к локальной переменной метода это наверное даже вообще абсурд, мне уже так хочется это называть. Пунктом 1 статьи идёт возможность возвращения более одного результата вычислений. Формально это не сделано, но фактически не-local поля в этой парадигме и выступают таковыми результатами, по совместительству с композицией.
Поэтому, ваше утверждение что "«автогенерация сеттеров и геттеров компилятором» ну нужна" кажется мне верным только если не исправлять опечатку «ну»
getA не будет если вам нужно просто значение переменной FStr, потому что указано read FStr
 property Str: string read FStr write SetStr;

Отличается тем что мне еще PStr надо обязательно get и set написать итого вместо 4 я написал один, и только потому что мне нужна была проверка.
Но вся прелесть в том что при прямом доступе к переменным я могу и писать как в Java (set/get все приватное) или через доступ напрямую… или же сочетать оба подхода с помощью property.
А чем будет отличаться обработка ошибок в неявно вызываемом методе от обработки ошибок в любом другом коде?
О том, что я могу реализовать любой механизм защиты от поступающих данных при использовании сеттера и мне не придется его дублировать. Я не позволю какому нибудь скедулеру запуститься в некорректное время. Я обеспечу целостность и адекватность поведения того кода, который я реализовал в своем классе.
Всё то же самое позволяют сделать и properties.
property диагностирует ошибку и всё, в чём проблема?

P.S. По крайней мере стало понятно почему никто не хочет ничего тут всерьёз обсуждать. Когда об «революции», то есть изобретении нового языка начинает рассуждать кто-то, кто вообще кроме Java ничего не видел — это не смешно. Это грустно.
Да я вообще маньяк порассуждать, я просто не могу строчить чаще чем раз в пять минут, а скоро меня заминусуют непонятно за что непонятно кто и раз в час только смогу:) Я правда вот так думаю, как там написал. Конструкции что-то диагностирующие наверное гуд, но сеттеров то они не отменяют, пока я не вижу способов как properties могут отменить сеттеры. Кроме того, я, зная себя, буду в сеттерах дублировать все проверки, если сеттеры понадобятся для чего-то ещё, такой я тормоз. Вообще, сильная стороны Java в том, что она для тормозов. Даже тысяча тормозов могут за очень много денег поддерживать очень тормознутый проект, и это реальная сила потому что многие компании имеют деньги, но не имеют гениев в своём штате. Революция устроил не я, а интервьювер, которому мне всё это в черновом варианте на английском пришлось строчить. Вот и вошёл во вкус.
Т.е, главный вопрос, который мне неясен. Как большое количество проверок может уживаться с работой с полем изнутри родного класса? В сеттере-то много всего, не будешь же это всё каждый раз проверять, особенно если у тебя по существу вложенные методы просто, а не большие объекты со словарями полей
Что же до дефолных значений, то и 0 денег по дефолту никого не обидит, если код вдруг начнёт кодить бывший гений после автокатастрофы. Тем более что в Яве int как назло неявно ноль по дефолту, я же как раз предлагаю уйти от этого неочевидного но вероятного — как раз задать явно этот нужный банку NPException по дефолту, архитектор-то у проекта был до автокатастрофы. В остальной куче проектов, в 99%, в которых просто зайчик на экран не успел подпрыгнуть и от того null, все эти важности совершенно ни к чему, они сильно тормозят и отнимают время. Даже для банков такая щепетильность не везде хороша. Видел я эти бесконечные
Exceptions по любому поводу в Грузинских банкоматах.
UFO just landed and posted this here
>Неопределённое поведение вместо NPE
Ну так по дефолту же всё требуется задавать и тоже инструменты есть чтобы не дать не задавать, не говоря у же о самих NPE, которые никто не мешает создать и использовать проекту во благо. Просто, по моему опыту, 90% этих NPE от того, что забыли инициализировать какие-то дефолты.
Это круто, статья не пропала даром, придёться изучать Scala — найду либо отличия, либо язык, на котором хочу писать.
Это описание чего угодно, только не Java.

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

Кажется самое страшное, что случалось с распространёнными языками — это переход от python2 к python3.

Visual Basic (переход от классической версии к Visual Basic .NET) — это не совсем то: после ~15 лет «клинической смерти» (когда программисты, пользовавшиеся классику уже ушли, а новых ещё было мало) он снова начал набирать популярность… Но это уже другие разработчики, они решают совсем другие задачи…
Лучший способ убить мой мозг это не понимать моих и так дурацких шуток.
UFO just landed and posted this here
Тоже считаю, что очевидный прогресс велосипедной техники невозможен без изобретений. Вкладывайтесь, я освою. Вообще, сторонние фреймворки хороши до определённого момента, после они очевидно тормозят развитие, но от них уже не отказаться. Любой большой проект уже содержит в себе новую семантику и свой фреймворк, иначе ему не стать большим, либо стать большим за нереальные деньги, видел я такой — собирается сутки. Тысяча кодеров по чуть-чуть копаются в меркуриале. Сколько это всё стоит мне страшно себе представить. Красота природы в том, что мне после этого проекта достался по наследству визуально очень похожий и по схожей тематике, но по началу я кодил его… один бедняжка. Т.е., по множеству функций, которые юзер дёргал, с первого взгляда он был не намного проще того гигантского. Конечно, это только с первого взгляда, но я был в начале и в конце лавины и это впечатляет. Технологии и в первом и во втором практически идентичные. Ява это очень солидный язык.
Модификатор «static» из языка убрать, в качестве static — дефолтный экземпляр, доступный по ссылке на типизированный null.

Какова практическая необходимость такого изменения? Вон, люди в Kotlin наоборот плюются от отсутствия static.


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

Т.е. структурная типизация как в go и typescript вместо существующей номинативной? А зачем?


Таким образом, больше не нужен «interface» как таковой, только «class», «abstract» — лишь инструкция, запрещающая создание экземпляра

Да, но как тогда вы в методе будете указывать, что переданный параметр должен обладать такими-то свойствами? Для этого нужно будет вводить синтаксис для описания анонимного типа-структуры. А потом и тайпалиасы прибегут (что и будет аналогом интерфейсов). Я кстати ещё молчу про перфоманс instanceof/checkast. Ну и да, это не просто языковое изменение, тут JVM придётся выкинуть.


Добавление правил слияния для «extends».

Ага, т.е. множественное наследование реализаций? А как будет реализовываться vtable? Через ужасные трюки как в C++, с нескольми указателями на vtable в каждом инстансе? С неизбежным ростом размера заголовка объекта? А как при этом обеспечивать identity equality?


Динамические типы. Темплейты как в Си, а не как женериксы в Java

И как это будет работать с variance? Изобретать неявные преобразования, как в C++?


Больше не понадобятся специальные ссылки на метод, как в Java 8. Ссылка на метод будет означать ссылку на объект.

Простите, а как при ссылке на overloded-метод должен резолвиться метод с конкретной сигнатурой?


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

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


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

Чтение за границами массива — это в 99% случаев не то, чего мы реально хотели, а значит — ошибка. Одним из принципов дизайна Java является fail fast, что, исходя из моего опыта (и из чужого опыта — тоже), является благом. Проблема производительности решается в данном случае JIT-компилятором, который может сделать range analysis и доказать, что проверка не нужна. Они, конечно, слабоваты эти range-анализы, но на практике если у вас такой сложный код, что в нём сложно что-то доказать, то в нём и проверка на выход за границу массива не будет узким местом.

1.
Какова практическая необходимость такого изменения? Вон, люди в Kotlin наоборот плюются от отсутствия static.

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

2.
Т.е. структурная типизация как в go и typescript вместо существующей номинативной? А зачем?

Высчитывать уникальный код множества ключей vtable, это собственный тип. Ключи из сигнатур, у нас же только методы. Значение — структуры объектов композиции. Полный тип класса — хэштаблица предков и своего типа, значение — структура, описывающая наследуемый объект, см. дальше. Т.е., в обоих таблицах value это ссылки на структуры, описывающие конкретные объекты. Классов на этом уровне просто нет. Методы — адрес в этой структуре. До оптимизаций один адрес, т.к. перегружаемые методы в vtable.

3.
Да, но как тогда вы в методе будете указывать, что переданный параметр должен обладать такими-то свойствами? Для этого нужно будет вводить синтаксис для описания анонимного типа-структуры. А потом и тайпалиасы прибегут (что и будет аналогом интерфейсов). Я кстати ещё молчу про перфоманс instanceof/checkast. Ну и да, это не просто языковое изменение, тут JVM придётся выкинуть.

В языке аргумент это просто примитив либо название метода, который класс по совместительству. Там нет ничего кроме классов, они же — методы с фигурными скобками. Для машины аргумент это ссылка на структуру объекта, в которой 1: vtable, в котором хеши из сигнатур (у нас же только методы) и значение-ссылка на такую же структуру другого объекта; 2: хэштаблица из хешей (2) предков и себя самого; и 3: адрес метода. instanceof просто ищет хеш во второй таблице.
JVM — не важно, я писал о языке вообще.

4.
Ага, т.е. множественное наследование реализаций? А как будет реализовываться vtable?

Не совсем множественное наследование. vtable состоит из key — хешкода сигнатуры метода, и value — ссылки структуру, описанную в (3), где есть и тело метода и такой же vtable и т.д. до победы. Т.к. в языке нет ничего кроме методов, они же классы. Как это заполнить? В случае конфликта требовать явно указать источник, а в случае некомплекта какого-то из предков — тип убрать из списка предков (2), но часть кода повторно использовать, т.е. оставить. equality — понятно уже как?

5.
Простите, а как при ссылке на overloded-метод должен резолвиться метод с конкретной сигнатурой?

Прямо по хешу сигнатуры метода из vtable. И есть куда оптимизировать простые случаи.

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

Просто свои приватные массивы и примитивы, они же лежат по адресу без всяких vtable. Чужие не надо. В пункте 13 даже есть заделка, но для этого и она наверное медленная.
Чтение за границами массива — это в 99% случаев не то, чего мы реально хотели, а значит — ошибка. Одним из принципов дизайна Java является fail fast, что, исходя из моего опыта (и из чужого опыта — тоже), является благом.

Ну, это вопрос философский. Я описал именно то, что хотел бы видеть. Мусор можно и в своём массиве найти. Чтение своей памяти это первый способ рефлексии, ну а ценные данные шифровать надо.
Изобретать неявные преобразования, как в C++?

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

Честно говоря, ответа ни на один вопрос я не получил.


Какова практическая необходимость такого изменения? Вон, люди в Kotlin наоборот плюются от отсутствия static.

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

Во-первых, тут вы оперируете слишком большим количеством терминов, которые мы с вами не согласовали (объекты по-умолчанию, внутренние объекты). Можете более подробно описать эти особенности дизайна вашего языка? Объекты по-умолчанию это что-то вроде object в Kotlin? Т.е. есть сущность "объект" а есть — "класс". Или всё является классом? Тогда каким образом конструируются объекты по-умолчанию для классов, у которых нет конструкторов без параметров. Во-вторых, вопрос был в том, зачем избавляться от static.


Т.е. структурная типизация как в go и typescript вместо существующей номинативной? А зачем?

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


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


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

В языке аргумент это просто примитив либо название метода, который класс по совместительству. Там нет ничего кроме классов, они же — методы с фигурными скобками. Для машины аргумент это ссылка на структуру объекта, в которой 1: vtable, в котором хеши из сигнатур (у нас же только методы) и значение-ссылка на такую же структуру другого объекта; 2: хэштаблица из хешей (2) предков и себя самого; и 3: адрес метода.

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


function foo(x: { bar(x: number): string }) {
    x.bar(23)
}

и сюда подойдёт инстанс любого класса, у которого есть bar. Если попробовать в foo засунуть что-то не обладающее методом bar нужной сигнатуры, то компилятор нарисует ошибку. Вот где в вашем языке определяется сигнатура метода? Как в ней описать требования к передаваемому в метод объекту, если нет интерфейсов?


Ага, т.е. множественное наследование реализаций? А как будет реализовываться vtable?

Не совсем множественное наследование. vtable состоит из key — хешкода сигнатуры метода, и value — ссылки структуру, описанную в (3), где есть и тело метода и такой же vtable и т.д. до победы

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


Простите, а как при ссылке на overloded-метод должен резолвиться метод с конкретной сигнатурой?

Прямо по хешу сигнатуры метода из vtable. И есть куда оптимизировать простые случаи.

Вопрос снова был про семантику а вы ответили реализацией. Хорошо, попробую вот так. Пусть у нас есть в Java такие объявления:


void foo(Integer x);

void foo(String x);

когда я пишу o.foo(23), компилятор точно знает, что надо вызвать первый метод, потому что он подходит по сигнатуре. Далее, если у нас все методы есть просто поля функционального типа, то такой резолв становится невозможен (например, он невозможен в JavaScript, где ровно такое поведение, и там похожая (но не такая же) штука реализуется уже в рантайме руками). Именно поэтому для ссылок на методы и в Java используется такой синтаксис, который используется (там по сигнатуре метода в целевом SAM-интерфейсе можно сделать резолв нужного метода). Вы предлагаете отказаться от перегрузки по сигнатуре? А если не предлагаете, то как разработчик должен сообщать компилятору, который из методов ему нужен?


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

Просто свои приватные массивы и примитивы, они же лежат по адресу без всяких vtable. Чужие не надо. В пункте 13 даже есть заделка, но для этого и она наверное медленная.

Ещё раз обратите внимание — практика показывает. На Java вполне можно просто в виде библиотеки это сделать. И никто не делает. Точнее, делают в виде наколенных поделок, которые приходится выбрасывать, потому что при усложнении проекта и появления дополнительных требований оказывается, что никакая автоматика не справится.


Изобретать неявные преобразования, как в C++?

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

Вопрос был про варантность generics, т.е. про возможность объявить такое:


void foo(List<? extends C> supplier, List<? super C> consumer);

у шаблонов в C++ с этим проблемы. Ну как проблемы, там это решается средствами, которых и близко нет в Java (и про которые вы так же ничего не сказали)

Для удобства немного перегруппировал вопросы.
Честно говоря, ответа ни на один вопрос я не получил.

судя по всему, структурная типизация, так?

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

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

Вопросы «зачем» наверное могу сформулировать развёрнуто, но сейчас это не в приоритете. Из гармонии сфер с моим опытом.

1.
Во-первых, тут вы оперируете слишком большим количеством терминов, которые мы с вами не согласовали (объекты по-умолчанию, внутренние объекты).

Да, статья писалась слишком быстро.
По поводу умолчания и статики. Отвлечёмся от терминологии, описывающей языки. На диске есть данные. Данные структурированы. Иерархически, релятивно ссылками композиции и наследованием. У нас есть runtime инструмент клонирования этих данных и манипуляции ссылками. Очевидно, что на диске статично лежат дефолтные данные.
Объекты по умолчанию прямо подгружаются с диска со всеми своими внутренностями, инструмент создания нового экземпляра просто клонирует эту вложенность, начиная с того уровня, который создаётся. Дефолтные значения копируются, abstract поля перед этим либо явно назначаются, либо выдают ошибку. Таким образом, любой объект может runtime сериализоваться в исходники согласно п. 13 статьи, а не в что-то бинарное, и обратно компилироваться уже как бы в дефолтный статичный объект, который физически от остальных объектов не отличается, он отличается только своим создателем-машиной и временем происхождения. Любому объекту в памяти можно добавить поле abstract и тогда его нельзя будет клонировать без определения этого abstract и будет выдаваться NPE при попытке считать значение abstract.

2.По поводу внутренностей.
В языке за пределами тела метода нет ничего объектного кроме методов, внешних и на одном уровне. Объект, объявленный внутри другого объекта — внутренний объект. Для внешних пользователей нет даже полей, поскольку они семантически внутренние объявленные объекты, это из-за подразумеваемых неявно, до перегрузки, объектов-сеттеров и объектов-геттеров. Для самого объекта и для его внутренних, в пределах прямой видимости, поля есть, все они — приватные. Прямая видимость определяется по иерархии. Язык запрещает обращаться напрямую к полям вне прямой видимости, т.е. к полям, которые определены в других детях своих родителей.
Программа это просто область памяти, часть которой компилятор разрешает дёргать прямыми со смещением ссылками, а часть — запрещает. Эта область памяти увеличивается по мере создания новых объектов. Компилятор все вызовы в пределах прямой видимости делает прямыми со смещением, за пределами — через vtable, т.е. любой внешний вызов это не вызов из прямой видимости и происходит только через vtable. В прямой видимости также лежит либо ссылка на дескриптор объекта, либо примитив, либо массив, который я не считаю нужным делать объектом.

3.Что называть классом объекта? Весь дескриптор, описанный в предыдущих ответах. Т.е., у каждого объекта кроме наследников ещё и два собственных типа. Первый высчитывается просто по сигнатуре метода. Второй — хеш всех сигнатур из vtable. Кто как назовёт эту типизацию?
Алгоритм я постарался описать подробно и работает он за конечное время, об этом алгоритме ещё раз в след параграфе про вызов метода.
Дальше Баньян, наследующий ни кого-нибудь, а аж собственные внутренние классы. В дескрипторе в первой таблице по хэшам сигнатур значениями дескрипторы Tree, Branch, Leaf, Cell и перегруженный Grow, и хэш-ключ типа из всех этих сигнатур. Во второй таблице, по своим ключам типа, дескрипторы самого Banyan и его предков Tree, Branch, Leaf и Cell. Так у него срослось.
void Banyan extends Banyan.Tree{
	void Tree() extends Tree.Branch{
		void Branch() extends Branch.Leaf{ 
			void Leaf extends Leaf.Cell{
				Cell[] cells; //тут массивы - не объекты
				void Cell(){
					void Grow(){ 
					}				
				}//Из этой клетки пошло деление-заполнение массива, она дефолтная и в массив её не обязательно. 
				@Override
				void Grow(){ 
				}
			};//Эта клетка произошла от зелёных клеток ростка, но решила быть веткой 
			@Override
			void Grow(){ 
			}
		}//Первый Branch это ствол. 
		@Override
		void Grow(){ 
		}
	}//Это старейший баян
	@Override
	void Grow(){ 
	}
}

4.Собственные типы могут совпадать, если совпадает ещё и хэш ключей vtable, то это либо просто клоны, не перезагружавшие общего предка, либо, другая крайность, счастливо совпавшие и не имеющие общих предков классы из разных реализаций. Могут они быть взаимонезаменяемыми? Вопрос архитектору, факт совпадения можно выяснить на любом этапе даже runtime, поскольку ссылки-значения отличаются частично или полностью.

5.
обладающее методом bar нужной сигнатуры, то компилятор нарисует ошибку. Вот где в вашем языке определяется сигнатура метода? Как в ней описать требования к передаваемому в метод объекту, если нет интерфейсов?


Описание на уровне языка выглядит точно так, как описание в Яве. Fruit first. При компиляции исходников, структура передаваемого в метод объекта бёрется из двух таблиц просто по Fruit. Первая собственная таблица компилятора — имена->сигнатуры. Вторая — сигнатуры->ссылки на структуры. Тип, интерфейс и класс и даже метод — «Fruit». Посредством этих двух таблиц мы получаем ссылку на нужную структуру и вперёд.
Вы предлагаете отказаться от перегрузки по сигнатуре?

Во-первых, это проблема только компилятора, которая разрешима, см. дальше, но лаконичнее всего конечно не перегружать, а обернуть.
void Foo(int x){
	void Foo(char* x){
	}
}

потому что базовая функциональность там скорее всего в int x, a текст в него конвертируется, либо наоборот, это по смыслу. Если же там вообще разная функциональность, то я считаю такую перегрузку вредной.
При необходимости выбирать метод нужно всего-то положить в первую таблицу компилятора (не дескриптора) не сигнатуру, а таблицу сигнатур, из которых выбрать, причём, это же только при компиляции.

Чтот такое Fruit? Фрукт. Как кто-то назовёт тип этого типа?

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

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

Так обыгрываютя только логически важные переменные. Чем больше будет ООП, вокруг таких переменных — тем лучше. Не случайно к проектам на Си в довесок идут всякие медленные скриптяги.
Производительность компенсируется тем, что работа с родными полями и полями прямой видимости происходит напрямую. Элементарные массивы я не стал бы делать объектами, ну и проверки границ при доступе убрал бы.
Простейший процедурный код там, где нужна производительность, компилятор может многое оптимизировать, если не будет внешних ссылок. Можно добавить модификатор private, который будет явно сообщать, что не local, но полное внешнее исчезновение.

Вопрос был про варантность generics, т.е. про возможность объявить такое:

Да, без специального синтаксиса для темплейтов будет уныло вбивать. Утилита даже и runtime создаст два новых объекта:
 void Supplier extends List{
//в котором перезагрузит все методы с Object, например Object Get(int index) на
   С Get(int index) extends Get(int){
	return (C)super.Get(index);
   }
 <...>
}
//и ещё один
 void Consumer extends List{
  Object Add(Object arg) extends Add(Object) throws TheObjectNotParentFor{
	if (! C.instanceOf(arg)){ //С это полноправный объект и arg - полноправный класс
		throw TheObjectNotParentFor(object, C);//catch сделано через методы, в статье описано в пункте 9
	}
	return super.Add(object);
  }
 <...>
}

Объявляем void foo(List<? extends C> supplier, List<? super C> consumer);
Ещё раз обратите внимание — практика показывает. На Java вполне можно просто в виде библиотеки это сделать. И никто не делает. Точнее, делают в виде наколенных поделок, которые приходится

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

п15. Чтоб можно было грабить корованы.


Очень наивные утверждения, ломающие обратную совместимость, либо рудиментарные, либо вообще не реализуемые ввиду возникающих побочных проблем. Из здравого я бы отметил только п.3 — properties. Легко реализуемо и сразу убирает огромное количество бойлерплейта.

Почитайте комментарии. По неизвестной науке причине properties не годятся.
Я читал комментарии. По неизвестной науке причине ни один из комментирующих не смог внятно объяснить, что такое property в Delphi. То есть все вроде знают, но объяснить внятно никто не может. Поэтому нет смысла читать весь тот флуд выше.

P. S. Мне не надо рассказывать, что это. Я отлично знаю, и в целом согласен, что появление Delphi-style property в Java пойдет ей на пользу.
Никакой совместимости там и не подразумевалось, написано было раз сто, как не подразумевалась, например, совместимость Java и Си. По поводу наивности, это абсолютно голословное утверждение, не подкреплённое ничем абсолютно. Обсуждающие зачем-то какие-то проперти накатали уйму текста, в то время как сам факт обсуждения этих пропертей в контексте статьи свидетельствовал о том, что никто из обсуждающих вообще не разбирался по существу ни в чём из написанного в статье. Никакого труда не составляло бы за всё это время по пунктам перечислить конкретные наивности и побочные проблемы, но их там просто нет или исчезающе мало.
Непонятность это конечно вина статьи, потому что подобные вещи не пишутся наверное в спешке, но тема специальная, для ценителей и смысла нет писать бессодержательные негативные комментарии, если просто тупо не разбирался. Желания разобраться не было, так как наводящих вопросов был ровным счётом один от товарища джависта в конце обсуждения. Народ просто гнал какую-то свою дельфийскую волну непонятно куда непонятно зачем.
Никакого труда не составляло бы за всё это время по пунктам перечислить конкретные наивности и побочные проблемы, но их там просто нет или исчезающе мало.

Мой коммент выше читали? Там как раз некоторые наивности разобраны и обозначены некоторые побочные проблемы.

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

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

Причём здесь Kotlin? Мне бы блог Lisp-а или Scala. Ещё блог SmallTalk интересно почитать. И Пролога в прологе. Не найти, и прочитать жизни не хватит. Процедурная часть в языке настолько отделена от ООП, который на что похож, на нововведения? Вы сами сказали, что он похож на рудимент, но это ваше личное мнение. Так отделён, что можно, наверное, куски ANCI Си вставлять продумав слегка фреймворк.
Возможность вернуть более одного результата из функции.

Это не удобно. Лучше возвращать структуру (частный случай — кортеж), а с ней уже делать что душе угодно. А не как в Го — примите и проигнорируйте.


Для «protected», «default» и «public» полей автогенерация сеттеров и геттеров компилятором, без автогенерации видимых сорцов, но с возможностью их перегрузки, «private» убрать из языка, поскольку «сами» поля все и так будут «private» без возможности перегрузки.

Фактически вы описали property.


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

Фактически вы описали структурную типизацию.


В результате, объект будет наследовать код и реализовывать все родительские типы.

Фактически вы описали множественное наследование.


не сложно организовать перепланировку расширением типа инстанса прямо runtime, сужение — запретить

Насколько я понял речь про ковариантность. А что насчёт контравариантности и инвариантности?


Подметоды как в Паскале.

Все, конечно, в курсое как там в Паскале. Замыкания что ли?


Конструктор класса будет телом метода по умолчанию, который будет создан во время компиляции, в соответствии с пунктом 2 «экземпляры по умолчанию». Больше не понадобятся специальные ссылки на метод, как в Java 8. Ссылка на метод будет означать ссылку на объект.

По описанию похоже на замыкания.


Новый оператор для runtime шифрования всяких примитивов

Какой алгоритм и размер ключа захардкодим?


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

То есть иногда читать мусор из других объектов, а иногда аварийно завершаться по access violation?


Кроме того, в методе «run» объекта Runnable, который передается в Thread, требовать явно вызывать метод, прерывающий поток, и, чтобы без этого была ошибка компиляции.

Какой поток? Зачем его прерывать? Речь о yield что ли?


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

И как сохранить часть этого графа? Если же предлагается сохранять его целиком, то проще и быстрее память сдампить, как это делается в SmallTalk.


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

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


Резюмируя: без примеров хотя бы псевдокода понять о чём вы говорите крайне сложно. А вообще, заходите к нам в телеграм, где мы иногда обсуждаем дизайн языков: https://t.me/lang_idioms

У меня сегодня хватило сил только на один большой ответ. Будет и второй.
Это не удобно. Лучше возвращать структуру (частный случай — кортеж), а с ней уже делать что душе угодно.
С какой точки зрения неудобно? Когда мне нужны просто int x, y, то мне всегда хочется написать int x, y = func();
int int func(){ return x, y; } чем что-то создавать, даже структуру.
Фактически вы описали property.
Может быть, но особенность изложенной мной модели в том, что в ней это и многое фигурирует неявно, без дополнительной поддержки.
Фактически вы описали структурную типизацию.
Может быть, но я не находил однозначного ответа в рамках доступных определений. Чуть выше, 7-го и 8-го числа, описал дескрипитор и набросал алгоритм.
Фактически вы описали множественное наследование.

Может быть, но существующие алгоритмы, для которых применим этот термин, наверняка работают не так, как описанный мной.
Насколько я понял речь про ковариантность. А что насчёт контравариантности и инвариантности?
Выше, 8-го числа, я описал алгоритм, по которому утилита должна поддерживать семантику void foo(List<? extends C> supplier, List<? super C> consumer); Хотя, это будет работать с некоторыми интересными ньюансами, потому что в дескрипторе не отражена иерахия наследования, а отражены только сами факты наследования. Иерархию можно при желании отследить, но, пока я не вижу безальтернативной необходимости её хранить, если вы видите — можете привести пример.
По описанию похоже на замыкания.
Подобных вроде похожестей тут слишком много, иначе я не стал бы писать статью, если бы существующие языки меня устраивали. Сейчас все языки как будто из одного конструктора, не находите?
Какой алгоритм и размер ключа захардкодим?
По самому минимуму для собственных примитивных массивов хватит. Не хочу портить свою и так ужасную репутацию грустными воспоминаниями о том безобразии, которое я встречал в реальных работающих проектах. У людей нет времени ни на что.
То есть иногда читать мусор из других объектов, а иногда аварийно завершаться по access violation?
Видимо, вместо этого пункта должно быть то, что это не язык в себе, а фреймворк ООП для процедурных языков. Какой там будет процедурный язык мне не важно, но предыдущий пункт фреймворком бы поддержал.
Какой поток? Зачем его прерывать? Речь о yield что ли?
Дело в том, что рабочий день не должен быть бесконечным даже у кремниевых организмов.
Потоку нужно не только поспать, но ещё и заняться насущными делами, типа принять-отправить message, выполнить возникшие из других потоков дела в Runnable, ну и вообще сменить обстановку, развеяться, во избежание появления всяких ненужных блокировок потоков энергии.
И как сохранить часть этого графа?
Почему часть, если у меня написано «весь», я это делал, хотя и часть — не неразрешимая проблема.
Если это будет не только лишь дерево, а именно произвольный граф, то боюсь ваш SQL получится очень не простым.
У меня наоборот получался слишком простой, но не полноценный, хотя, может быть, у меня на полноценный просто времени не было. Вообще, стремиться нужно к возможностям экспертной системы, а куда стремиться-то в этой жизни?
Резюмируя: без примеров хотя бы псевдокода понять о чём вы говорите крайне сложно.
Там выше «баян баньян» есть, это и будет рабочим названием.
А вообще, заходите к нам в телеграм, где мы иногда обсуждаем дизайн языков: t.me/lang_idioms
Спасибо, зайду
Насколько я понял речь про ковариантность. А что насчёт контравариантности и инвариантности?

В дескрипторе за инвариантность отвечает тип2, хэш всех сигнатур из таблицы сигнатуры->дескрипторы. Нужный интерфейс может появиться в результате слияния любых дескрипторов по любому правилу, какое пожелает программист, это стоит как-то регулировать? Все предки во второй таблице тип2 -> дескриптор. Ньюанс пока вижу лишь неинтересный, в том, что super-у нужно явно указать предка, если предков больше, чем один, а если предок на этапе компиляции один, то компилятор укажет сам. Что там ещё может быть пока не соображу.
Даже и вовсе без особенностей, т.к. на этапе компиляции иерархию отследить не затратно
Sign up to leave a comment.

Articles