, но может не все заметили.
А тема, скажем прямо, интересная.
Я в ней только начал разбираться, так что ниже — несколько сырых и неоформленных мыслей.
1. Прототипное программирование — такой способ программирования, при котором новые объекты создаются не как экземпляры "классов", а как клоны существующих объектов; объекты могут изменяться на лету (например, добавлять методы); никаких концепций, кроме "объекты и сообщения" не существует — нет классов, нет наследования, нет синглетонов, нет ложки. Прототипное программирование — не "такое криво сделанное ООП", а другая парадигма.
2. Языки, которые изначально создавались как прототипные — Self (клон Smalltalk) и JavaScript. Тем не менее, в прототипном стиле можно программировать на многих других ОО языках; достаточно, чтобы язык позволял: а) модификацию объекта на лету и б) клонирования объекта, которое создает копию объекта, включая все методы, добавленные на лету (например, Ruby)
3. В "истинно прототипном" программировании, новый объект, созданный путем клонирования, "забывает" о "родителе". Тем не менее, и в Self, и в JS различными способами имитируется "что-то вроде наследования" ("я помню о своем прототипе, и если не знаю как обработать сообщение, прошу его это сделать"). Из чего можно сделать определенные выводы. И я от них не удержусь!
Выводы, практически ничем не обоснованные
Prototype-based programming — интересная и полезная парадигма. Наиболее полезна она в совмещении с "традиционным", class-based OOP.
"Линия совмещения" имхо, проходит вот где:
* если данный объект, по своей роли, это "данные + некоторые методы их обработки" (данные важнее методов, объектов огромная куча, они хранятся списками и обрабатываются все как один) — то ему "полезнее" быть экземпляром класса (особенно, если классы открытые — в Ruby можно в любой момент добавить метод в класс Integer, и все Integer'ы в программе будут иметь этот метод).
* если данный объект — это "поведение + нужные для него данные" (данные без методов мало что значат, объект один или несколько, может иметься большая иерархия наследников, несколько модифицирующих поведение) — то для него вполне осмысленным может оказаться prototype-based подход (глупый пример: кинуть на форму Editbox, склонировать его, добавить синенькую кнопочку — создавать класс "editbox с синенькой кнопочкой" ради единственного применения — как бы overhead).
Здравствуйте, c-smile, Вы писали:
CS>А хотелось бы еще иметь возможность создавать произвольные объекты (т.е. еще нужен typename):
CS>Пока такие варианты: CS>
CS>var obj = {=myclass one:1, two:2 };
CS>
CS>или CS>
CS>var obj = {:myclass one:1, two:2 };
CS>
CS>Как лучше? И скажем как это сделано в Ruby? Говорят он изумительно хорош в нотации
Погляди Эрлэнг. Там нечто похожее есть. Конкрено см. понятие литерала.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, c-smile, Вы писали:
CS>Как лучше? И скажем как это сделано в Ruby? Говорят он изумительно хорош в нотации
В питоне просто вызов конструктора базового метакласса:
>>> o = type("MySuperClass", (),{'msg': 'my super class'})
>>> o
<class '__main__.MySuperClass'>
>>> o.msg
'my super class'
>>>
Первый параметр имя класса, второй тупл предков, третий словарь с членами. При этом создается новый класс, если нужен его экземпляр справа просто добавляется пара скобок (с необязательными параметрами).
Здравствуйте, c-smile, Вы писали:
CS>Как лучше? И скажем как это сделано в Ruby? Говорят он изумительно хорош в нотации
В Ruby для похожих целей есть классы Struct, OpenStruct. А прямо в описании стандартного класса OptionParser приводится пример непосредственного использования OpenStruct и, как я сейчас узнал, prototype-based programming
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Io is a small, prototype-based programming language. The ideas in Io are mostly inspired by Smalltalk (all values are objects), Self (prototype-based), NewtonScript (differential inheritance), Act1 (actors and futures for concurrency), LISP (code is a runtime inspectable/modifiable tree) and Lua (small, embeddable).
Здравствуйте, c-smile, Вы писали:
CS>А хотелось бы еще иметь возможность создавать произвольные объекты (т.е. еще нужен typename):
CS>Пока такие варианты: CS>
CS>var obj = {=myclass one:1, two:2 };
CS>
CS>или CS>
CS>var obj = {:myclass one:1, two:2 };
CS>
CS>Как лучше? И скажем как это сделано в Ruby? Говорят он изумительно хорош в нотации
Что-то я не понимаю, чего ты сказать пытаешься. Можешь тот же самый код словами рассказать?
Здравствуйте, c-smile, Вы писали:
CS>Ты ничего не перепутал? Ты именно Erlang имеешь ввиду?
Ничего не путаю.
CS>Что-то я не помню в Erlang классов, а тем более объектных литералов.
А ты погляди кау у них записи делаются. У них есть ничего не значащие атомы (идентификаторы начинающиеся с маленькой буквы). Этими литералами можно задавать поля в кортэжах. А далее по ним же можно идентифицировать тип кортежа. Кортеж с некретным атотмом интерпретируется как запись. В итоге получается эдакое динамическое формирование новых типов.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, c-smile, Вы писали:
CS>>Ты ничего не перепутал? Ты именно Erlang имеешь ввиду?
VD>Ничего не путаю.
CS>>Что-то я не помню в Erlang классов, а тем более объектных литералов.
VD>А ты погляди кау у них записи делаются. У них есть ничего не значащие атомы (идентификаторы начинающиеся с маленькой буквы). Этими литералами можно задавать поля в кортэжах. А далее по ним же можно идентифицировать тип кортежа. Кортеж с некретным атотмом интерпретируется как запись. В итоге получается эдакое динамическое формирование новых типов.
Здравствуйте, c-smile, Вы писали:
CS>...Реально же нужно изобрести короткую запись вот этого (работает и так): CS>
CS>var P = { prototype:Person, name:"Joe", phone:[0,8,2] };
CS>
CS>я пока остановился на CS>
CS>var P = {:Person name:"Joe", phone:[0,8,2] };
CS>
CS>а там посмотрим.
Подожди. Person — это что? Имя новой сущности или уже существующей? Если уже существующей (которая используется в качестве прототипа), то в Io, к примеру, это выглядит так:
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>Здравствуйте, c-smile, Вы писали:
CS>>...Реально же нужно изобрести короткую запись вот этого (работает и так): CS>>
CS>>var P = { prototype:Person, name:"Joe", phone:[0,8,2] };
CS>>
CS>>я пока остановился на CS>>
CS>>var P = {:Person name:"Joe", phone:[0,8,2] };
CS>>
CS>>а там посмотрим.
ЗХ>Подожди. Person — это что? Имя новой сущности или уже существующей? Если уже существующей (которая используется в качестве прототипа), то в Io, к примеру, это выглядит так: ЗХ>
На самом деле prototype в JavaScipt это не прототип для копирования. Это ближе к ссылке на класс.
Объект есть "вешалка" для атрибутов — нечто типа hash table. Каждая такая вешалка имеет спец. поле: __proto__ — ссылка на вешалку-родитель (класс?).
Конструкция:
function MyClass() { this.one = 1; }
var p = new MyClass();
в чистом JavaScript это следующее:
1) сконструировать объект-вешалку.
2) положить в её поле __proto__ ссылку на объект MyClass.prototype.
3) вызвать функцию MyClass с this установленным на эту вновь созданную вешалку которая пуста к этому моменту.
4) функция MyClass должна уже создать набор instance variables ( this.one = 1 )
Переменная __proto__ таким образом выполняет функцию связки instance объекта и его "класса" — в C++ самый близкий аналог этому — указатель на vtbl для объектов виртуальных классов.
Т.е. если скажем мы имеем такую дефиницию:
MyClass.prototype = function foo() { alert(this.one); }
то p.foo() означает сдедующее:
1) найти атрибут с именем foo на вешалке в p.
2) если такой атрибут не найден искать в вешалке p.__proto__ и т.д.
3) если атрибут найден и есть функция позвать её с this === p.
4) иначе — ошибка — no such method.
Т.е. get attribute value это "глубокий" поиск по цепочке __proto__ полей.
Set attribute value это другая история:
p.foo = function () { ...} означает сдедующее:
1) в атрибут с именем foo на вешалке в p записать ссылку на функцию в правой части.
2) и все, в JS v.1.3 и ниже __proto__ в этом процессе не участвует.
JS v.1.4 эта операция тоже "глубокая" — выполняется сначала глубокий поиск на предмет
нахождения функции setter с таким именем.
Вот примерно такая механика. Мне представляется она несколько искусственно запутанной если честно.
И я её упростил.
Здравствуйте, c-smile, Вы писали:
CS>Курс молодого JavaScript бойца:
CS>На самом деле prototype в JavaScipt это не прототип для копирования. Это ближе к ссылке на класс.
На самом деле, в реальных прототипных языках (которым и посвящен топик, кстати) — это тоже так и есть. Т.е. есть "специальные объекты-прототипы", которые и клонируются. В JS зачем-то решили избежать понятия клонирования, и замаскировали его под создание объекта. Но по сути — все так же, как в Self и Io. Но порождает кучу синтаксических идиотизмов с двойственностью Class / Class.prototype
CS>Объект есть "вешалка" для атрибутов — нечто типа hash table. Каждая такая вешалка имеет спец. поле: __proto__ — ссылка на вешалку-родитель (класс?).
Опять же, ровно как в Self/Io.
CS>Конструкция: CS>
CS>function MyClass() { this.one = 1; }
CS>var p = new MyClass();
CS>
CS>в чистом JavaScript это следующее:
CS>1) сконструировать объект-вешалку. CS>2) положить в её поле __proto__ ссылку на объект MyClass.prototype. CS>3) вызвать функцию MyClass с this установленным на эту вновь созданную вешалку которая пуста к этому моменту. CS>4) функция MyClass должна уже создать набор instance variables ( this.one = 1 )
Опять же, все повторяется. Просто в языках, которые не притворяются class-based, это выглядит несколько более consistent:
MyClass = Object.clone(
one := 1
foo := method(....)
)
p = MyClass clone
CS>Переменная __proto__ таким образом выполняет функцию связки instance объекта и его "класса" — в C++ самый близкий аналог этому — указатель на vtbl для объектов виртуальных классов.
угу.
CS>Т.е. если скажем мы имеем такую дефиницию: CS>
CS>MyClass.prototype = function foo() { alert(this.one); }
CS>
CS>то p.foo() означает сдедующее:
CS>1) найти атрибут с именем foo на вешалке в p. CS>2) если такой атрибут не найден искать в вешалке p.__proto__ и т.д. CS>3) если атрибут найден и есть функция позвать её с this === p. CS>4) иначе — ошибка — no such method.
угу.
CS>Т.е. get attribute value это "глубокий" поиск по цепочке __proto__ полей.
CS>Set attribute value это другая история:
CS>p.foo = function () { ...} означает сдедующее:
CS>1) в атрибут с именем foo на вешалке в p записать ссылку на функцию в правой части. CS>2) и все, в JS v.1.3 и ниже __proto__ в этом процессе не участвует.
CS>JS v.1.4 эта операция тоже "глубокая" — выполняется сначала глубокий поиск на предмет CS>нахождения функции setter с таким именем.
угу. (кста, в Io есть два типа присваиваиния — := работает как в старом JS, а = — как в новом)
CS>Вот примерно такая механика. Мне представляется она несколько искусственно запутанной если честно. CS>И я её упростил.
Почему запутанная — мы как бы выяснили. Теперь, если можно, покажи как эти все примеры (приведенные выше) выглядят у тебя — только код. К слову сказать, "фикся" JS, ты по сути отбрасываешь возможность использования существующих JS-библиоте — нет?
Здравствуйте, c-smile, Вы писали:
CS>что в принципе близко к тому что хочется. Реально же нужно изобрести короткую запись вот этого (работает и так): CS>
CS>var P = { prototype:Person, name:"Joe", phone:[0,8,2] };
CS>
CS>я пока остановился на CS>
CS>var P = {:Person name:"Joe", phone:[0,8,2] };
CS>
CS>а там посмотрим.
А почему не что-нибудь более тупое?
//это практически руби :)
var P = Person.new{name:"Joe", phone:[0,8,2] };
//а это почти С++
var P = new Person(name = "Joe", phone = [0,8,2]);
//я еще по всякому могу :))
var P = Person clone (name => "Joe", phone => [0,8,2]);
var P = Person[name: "Joe", phone: [0,8,2]];
var P = Person.name("Joe").phone([0,8,2]);
Короче, суть в том, чтобы отделить имя-чего-создаем (Person) от аргументов (name, phone)
Здравствуйте, c-smile, Вы писали:
CS>...а там посмотрим.
Идея Эрлэнга хороша свой простотой и универсальностью. Его авторам достаточно было сделать кортэжи и атомы, а стальное получилсь из этого базиса само собой. Но Эрлэнг ориентируется на паттерн-мтачинг (ПМ). Сам оп себе ПМ ведь замечательная, но в Эрланг им предлагается заенять полноценный доступ к членам записей. Что на мой взгляд является откровенным промохом.
Но ты можешь взять за основу идею и реализовать ее более грамоно.
Я же предпочитаю классические классы и компиляцию.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>Почему запутанная — мы как бы выяснили. Теперь, если можно, покажи как эти все примеры (приведенные выше) выглядят у тебя — только код. К слову сказать, "фикся" JS, ты по сути отбрасываешь возможность использования существующих JS-библиоте — нет?
Это вот чистый JS: CS>>Конструкция: CS>>
CS>>function MyClass() { this.one = 1; }
CS>>var p = new MyClass();
CS>>
CS>>в чистом JavaScript это следующее:
CS>>1) сконструировать объект-вешалку. CS>>2) положить в её поле __proto__ ссылку на объект MyClass.prototype. CS>>3) вызвать функцию MyClass с this установленным на эту вновь созданную вешалку которая пуста к этому моменту. CS>>4) функция MyClass должна уже создать набор instance variables ( this.one = 1 )
Это TIScript:
Объект есть "вешалка" для атрибутов — нечто типа hash table.
Каждая такая вешалка имеет спец. поле: prototype — ссылка на вешалку-родитель (класс).
Конструкция:
var MyClass = {}; // или var MyClass = {:MyBaseClass};
function MyClass.constructor() { this.one = 1; }
var p = new MyClass();
в TIScript это следующее:
1) сконструировать объект-вешалку.
2) положить в её поле prototype ссылку на объект MyClass.
3) Вызвать функцию MyClass.constructor с this установленным на эту вновь созданную вешалку которая пуста к этому моменту.
4) функция MyClass.constructor должна уже создать набор instance variables ( this.one = 1 ) и/или вызвать конструктор суперкласса — super(...)
Т.е. TIScript ближе к Java в этом смысле чем JavaScript.
Как иллюстрация сего : схема динамического субклассинга используемого в Sciter:
Т.е. в Sciter CSS позволяет декларативно назначать DOM элементам разные скрипт классы.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, c-smile, Вы писали:
CS>>Как лучше? И скажем как это сделано в Ruby? Говорят он изумительно хорош в нотации
E>В Ruby для похожих целей есть классы Struct, OpenStruct. А прямо в описании стандартного класса OptionParser приводится пример непосредственного использования OpenStruct ...
Struct и OpenStruct понятны.
Непонятно другое: зачем надо было делать hash map и скажем объект разными сущностями.
ПММ, в JS это красивее сделано (если забыть про кашу с __proto__).
E>... и, как я сейчас узнал, prototype-based programming
CS>>...а там посмотрим.
VD>Идея Эрлэнга хороша свой простотой и универсальностью. Его авторам достаточно было сделать кортэжи и атомы, а стальное получилсь из этого базиса само собой. Но Эрлэнг ориентируется на паттерн-мтачинг (ПМ). Сам оп себе ПМ ведь замечательная, но в Эрланг им предлагается заенять полноценный доступ к членам записей. Что на мой взгляд является откровенным промохом.
VD>Но ты можешь взять за основу идею и реализовать ее более грамоно.
Ууу. Records в Эрланге — это отдельная песнь. Ее уже лет 10, как пытаются заменить на более стройную, но пока не получается. Там много корявостей с этими записями. См., например, [Erlang] Recless
Здравствуйте, c-smile, Вы писали:
CS>Это вот чистый JS: CS>>>Конструкция: CS>>>
CS>>>function MyClass() { this.one = 1; }
CS>>>var p = new MyClass();
CS>>>
CS>Это TIScript: CS>
CS>var MyClass = {}; // или var MyClass = {:MyBaseClass};
CS>function MyClass.constructor() { this.one = 1; }
CS>var p = new MyClass();
CS>
Поздравляю, Шарик! Ты меня окончательно запутал!
Коль скоро ты уже сделал из prototype-based языка class-based, зачем тебе то, с чего ты начал обсуждение? Вот это вот:
//зачем тебе
var P = { prototype:Person, name:"Joe", phone:[0,8,2] };
//если у тебя уже есть?
var P = new Person();