Pull to refresh
61
0
Viktor @wentout

системный анализ

Send message

Согласен про "прозрачность и понятность".

То, что такой код "дурно пахнет" -- фрукт Дуриан тоже пахнет дурно )
Вы привели пример так называемого наследования на основе классов, которое в сути своей есть просто декомпозиция процесса конструирования, так как происходит синхронно и в целом ничем не отличается от наследования на основе функций, так как в результате ровно всё то же самое можно сконструировать и там. Это не синтаксический сахар, т.к. воспроизвести функциональность классового наследования на основе функционального является весьма непростой задачей, нетривиальной.

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

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

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

- Имеется возможность затащить Строгую Типизацию в Runtime для полей объектов. Пишется в 50 строк примерно. И само по себе -- то уже очень много, по сути можно повторить всё то, что есть Ahead Of Time в Type Script и, таким образом, существенно улучшить качество кода

- Множественное наследование на основе отношений линейного порядка у нас и так уже есть, соответственно mixins & traits реализуются максимально явно без необходимости умственных усилий. Но! Важно понимать -- что это они и есть.

- Со строгой типизацией и наличием оператора instanceof открываются возможности в Pattern Matching, а фунциональная природа языка даёт почти всё, что необходимо для Lambda Calculus и т.п. и т.д.

- Конструкторы, наследуемые от Экземпляров -- это так называемые Зависимые Типы, и, соответственно, они таким образом выразимы не только лишь в TS, но и в непосредственно JS Runtime.

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

Конечно же, это всё больше "теоретическое программирование". Но практики нет не потому, что она невозможна, а как раз потому, что все думают, что она невозможна или не знают, что возможна. И, как говорится -- "что имеем". Хотя, безусловно, возможно что мы получили то "за что боролись" и заслуживаем имено такого отношения к JS, какое есть.

И, вот только вот моя личная с этим мнением несогласность и остаётся.

Допустим, что у нас в JS строгая типизация для объектов, сделать это очень просто, с учётом нынешних фич языка в метапрограммировании: get/set + Proxy.
То есть все свойства объектов проверяются на instanceof , а значит:

  • Можно сделать так, что в свойствах объектов будет разрешено хранить только объекты. То есть даже для примитивов числа, строки и булевы можно будет хранить как экземпляры их конструкторов: new Number, new String, new Boolean, или производные этих конструкторов через Class Extends

  • Таким образом для "строгости" вводится правило, что положить новое значение свойство объекта можно лишь в том случае, если это значение того же самого типа. Для этого достаточно сравнить образец, уже существующий в свойстве с тем, что нам поступает в value для set -ера. И тут мы вольны выбирать можно ли для Extended полей класть базовый тип или нет, но в целом это легко пишется.

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

И тогда, получается, если вам нужны рекурсивные конструкторы в JS, то их можно получить как в примере из статьи. Но лучше взять тот GIST, там все нюансы учтены.

в вашем примере чуть иначе можно, и всё получится:

const ogp = Object.getPrototypeOf;

class MyClass1 {
	get name () {
		return 'MyClass1';
	}
}

class MyClass2 {
	get name () {
		return 'MyClass2';
	}
	constructor () {
		const proto = ogp(this);
		Object.setPrototypeOf(proto, new MyClass1);
	}
}

const obj = new MyClass2();

console.log(obj);								// MyClass2 {}
console.log(obj.name);							// MyClass2
console.log(obj.hasOwnProperty('name'));		// false
console.log(ogp(obj).hasOwnProperty('name'));	// true

// -----------------------------------------------------------

console.log(ogp(ogp(obj)).name);				// MyClass1

// -----------------------------------------------------------

console.log(obj instanceof MyClass1);			// true 
console.log(obj instanceof MyClass2);			// true

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

эмм... наверное потому, что без customElements.define не получится сделать, т.к. нужно зарегистрировать кастомный HTML элемент, иначе придётся модифицировать базовые, а это не то, чтобы вообще правильно, не знаю, может попробую когда-нибудь поэксперементировать, вообще, скорее всего, можно и их модифицировать, там нет рекурсии, конечное количество глубины, просто, вот это точно громадный impact по производительности будет, но если там нет запретов, то можно порвать и эти цепочки

Современный Front-End где нужно знать 100500 библиотек и фреймворков только чтобы страничку отрендерить -- это сложно, переусложнено. Нужно было как-то на реакт что-то сделать очень простое. С .props было лень разбираться, со стейтами и т.п., прям лень. Добавил общую зависимость, подписался на неё и вызывал .render по мере необходимости. Коряво, но элементарно, уложился в пару часов. Но вообще да, всё браузерное API выучить, видимо, уже практически не реально, разве что если писать это самое API со стороны писателей API или стандартов, то есть когда это работа. Видимо поэтому все библиотеки и плодятся, упрощая работу в какой-то своей привычной авторам "атмосфере".
А так Custom Elements -- это тоже своего рода API -- то есть Application Programming Interface )

Думаю не представляет никакого интереса. Уверен они точно знают, что так можно и в этом нет ничего такого уж необычного. Выйти "за границы" не получится, оно всё равно в рамках JavaScript останется, а "базовую логику" я не трогал, просто инкапсулировал. Полиморфизм добавлен "сверху", без мутации, так что смысла в споре не вижу.

Зачем ? Практического смысла не много, да и вряд-ли кто-то после прочтения статьи прям "бросится" писать код на основе этой идеи. В вашем примеме радикальных отличий от моего примера только те, что Proxy у вас не используется. Так что спорить смысла тоже не вижу )

А, ну и да, вы же неправильно сделали, поэтому и получили ошибку, вот так нужно:

class MyHTMLElement extends HTMLElement {
  constructor() {
    super();
    const root = Object.getPrototypeOf(this);
    const props = {
      // тут можно дополнительных свойств накидать
      // которые нужно проксировать
    };
    Object.setPrototypeOf(props, root);

    // в этом месте в this можно накидать свойств
    // которые не будут обрабатываться proxy
    
    const proxy = new Proxy(props, { ... });
    Object.setPrototypeOf(this, proxy);

    // а с этого места всё новое в this будет идти через proxy    
  }
}

Если так сделаете то вашей ошибки не будет )

Предыдущй мой коментарий -- это сарказм )

В API Custom Elements ни слова про Proxy.
API для отслеживания свойств в DOM совсем другое, и к JS имеет такое же отношение, как customElements.define , мне всё же хотелось в примере быть ближе именно к JS.

А вообще я Back-End пишу на Node.js, в браузер смотрю для чтения статей, решил, вот написать что-то, что может быть кому-то интересно, т.к. прикладные навыки с Front-End остались в эпохе jQuery.

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

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

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

Да, просто если хочется видеть сами вызовы .setAttribute и .removeAttributе и т.п., то можно и так, думаю. Про размер объекта -- не уверен, что это как-то влияет, объект --это же всего лишь указатель, поэтому какая разница что там "под" прокси. Количество свойств, безусловно влияет если мы хотим их "перебирать", но они же все в этом самом объекте, то есть хешированы и доступ к ним "атомарный". Другое дело, что в самом деле само проксирование -- дорогая операция + поиск свойств вглубину же будет, и чем на более низжем уровне они лежат, тем дольше. А так -- да, конечно, спасибо за дополнение!

Да, с React Component тоже самое можно провернуть )

Да, так понятней, Спасибо!
Мы про одно и то же, с разных сторон, получается, написали )

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

да, и это достаточно просто объяснить TypeScript-у, буквально одна строка

export type Proto<P, T> = Pick<P, Exclude<keyof P, keyof T>> & T;

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

да как и вся информация, кому-то нужна, кому-то нет
она про JavaScript, поэтому в этом потоке

да, согласен, учту, мои "оправдания" никому не интересны, всем всё равно, у всех своих хватает страданий и мыслей и т.п. +1 коментарию.

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

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

Так что, категорично объяснять как себя "вести в приличном обществе" -- оно, конечно, безусловно -- вполне можно, но вести или нет -- это выбор персонажа.

Так а про сам результат что скажете ? Стиль понятно, страдает. Результат вам как, понравился? Или вы это раньше уже видели?

Совершенно верно. Это то, как есть сейчас. С этой логикой нет никаких существенных проблем. Её обычно на всё хватает. Этим пользуются, к тому же есть private модификаторы в самом JS уже тоже.

Просто вы описываете Назначение Экземпляра.

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

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

Ааа, вы про это. Да, есть такое ограничение для прямых указателей, т.е. в JS пока нет перегрузки операции Assign.
Но если работать с полями объектов через get-еры/set-еры и добавить чуточку магии мета-программирования для Symbol.toPrimitive, то всю TS логику ограничений на типы можно примерно в 50 строк компактненько добавить в Runtime, т.е. добавить строгость и ограничения прям в туда. Таким образом на TS логика не изменится, а логика JS в Runtime'е будет ей соответствовать.
С учётом того, что, например IEEE 754 в целом отражает состояние вне зависимости от языка -- это особенно полезно будет запрещать творить дичь, складывать примитивы с объектами и т.п.. В общем строгая типизация со всеми её плюсами и минусами для полей объектов вполне возможна.
То есть мы можем сделать так, что в полях объектов для Данных хранятся Объекты. И то есть можно получить вполне себе Номинальную Типизацию. Но да, только для полей объектов. Но не очень сложно ведь. И объяснение "как" простое -- через Symbol.hasInstance проверять является ли экземпляр наследником класса MyNumber, и, таким образом всё становится намного интересней, веселей и действительно гибким, гораздо более динамическим чем исходный такой весь из себя "динамический" JS.
И вот тут уже становятся весьма необходимыми все эти преимущества языков, считающихся более развитыми, т.к. мы в самом деле уже давно можем это не просто смоделировать, а более того -- смоделировать так, как нам нужно.
Т.е., такой "дизайнерский" JavaScript под задачи конкретного проекта. Нужна нам, допустим, System F -- да пожалуйста. Нужна λC -- да тоже без проблем, конечно, забирайте, грузите вагонами: https://en.wikipedia.org/wiki/Lambda_cube

Не уверен, что понял, про что речь.
Вы имеете в виду Type Families, где все классы являются наследниками одного исходного экземпляра ? https://en.wikipedia.org/wiki/Type_family

да, примерно так )
мне хотелось отразить муки творчества )
но, да, +1 к комментарию )

1
23 ...

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity