After reading the OOP section of falcon survival guide, especially the
"hacking-like"
prototype model in falcon, I raised some thoughts, which, personally,
looks more
consistent in language syntax level. I would like to post it here, to
request for
comments :-)
First, I'd like to refer a paper on this topic:
http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.html
And the basic concepts:
http://en.wikipedia.org/wiki/Class-based_programming
http://en.wikipedia.org/wiki/Prototype-based_programming
And here are the thoughts:
* The paradigm should support both Class-based OOP and Prototype-
based OOP;
* Basic elements in this paradigm are: <object>, <class>,
<instance>;
* <object> is the basic element, an <object> consists of
properties and methods;
* <object> implements the "prototype" concept in Prototype-based
OOP;
* <object> is standalone and unique, and is not an <instance> of
any <class>;
* Example syntax of an <object> definition:
object falcon
creator = 'Giancarlo Niccolai'
version = '0.9.6.2'
paradigms = ['procedure', 'object-oriented', 'functional',
'message', 'tabular']
function introduce()
> 'Falcon is created by ' + self.creator + ', current
version is ' + self.version
end
end
* <object> can derive from another <object>, for example:
object python
// properties and methods for python
end
object lua
// properties and methods for lua
end
object falcon from python, lua
// properties and methods for falcon
end
Here, falcon inherits properties and methods from both python
and lua.
Inside the syntax, falcon maintains a prototype-chain, which
support accessing
missing properties and methods following the chain. This is
called "delegating";
* <class> is a special <object>, it can be instanciated to create
many <instance>s;
* <class> abstracts common properties and methods of a group of
<instance>s;
* <instance> is NOT <object>, and <object> is NOT <instance>
either;
* Example syntax of a <class> definition:
class committer(name)
// class property
static project = 'falcon'
// instance member
name = nil
init
this.name = name
end
// class method
static function get_project()
return self.project
end
// instance method
function say()
> 'I am ' + this.name + ', a committer of ' +
self.get_project() . ' project'
end
end
jonnymind = committer('Giancarlo Niccolai')
jonnymind.say()
* <class> can derive from another <class>, for example:
class web_committer(name, category) from committer(name)
// class property
static svn_repository = 'svn://falcon.org/...'
// instance property
category = category
// instance method
function say()
super.say()
> 'And I work in ' + this.category . ' sub-project'
end
end
diogin = web_committer('Jingcheng Zhang', 'nest')
diogin.say()
Of course, <class> can inherits from multiple <class>es just
like <object>;
* So, properties in <object> are equivalent as static properties
in <class>,
and methods in <object> are equivalent to static methods in
<class>, they
all follow the rules of delegation to access the prototype-
chain;
* <object> members or static members of <class> are referred with
"self.",
and <instance> members are referred with "this.", which helps
developer
differentiate the member-level easily.
Your ideas have some interesting points, which I'd like to keep into
consideration; read below.
However, let me warn the audience about one very practical
consideration. In my short and limited experience as a language
designer, I have noticed that special syntax addressing a special
problem is not always helpful as it may seem at first glance.
For example, LISP, one of the most powerful languages of all times, has
nearly zero special syntax constructs, and a very simple set of basic
syntax elements.
Conversely, languages having special syntax for everything tend to be
rather "rigid". Often, problems resolved with special syntax constructs
have been better profiled and resolved through a "function library"
approach. Consider rules and constraint programming as solved by Prolog
and as solved by C, Java and Python libraries. Despite the fact that the
former was specifically meant to address that particular class of
problems, the same problems expressed through "rules" object sets are
even more readable and compact.
A still valid reason to provide special constructs may be assistance of
the compiler in optimizing during compile time. However, modern
approaches to compilation process has lessened this need; consider, just
for instance, the special Python function "min(a,b)", which is resolved
at compile time into VM opcodes and doesn't generate a real call to a
function called "min".
Considering all this, the "hacking" approach to prototype OOP doesn't
seem so "hacking" at all. Conversely, caging prototype OOP into a
language-defined set of structures may deprive the final users of some
potential that make POOP so interesting. In Falcon, Prototype OOP can be
"programmed" from the inside and the outside, with the ability to
dynamically change the structure of prototype "objects", (let's call
them b-dict, blessed dictionaries, to avoid confusion with "instances"),
without the need of special grammar to address this problem. Forcing a
grammar into the current model would inevitably give off some of the
potential of this paradigm, trading it off with a "clarity" which may be
more a matter of personal taste than of an objective advantage.
Also, "grammarizing" the approach to prototype OOP seems to go against
the modern 5th generation languages tendency which goes on the other
direction. Consider the case of Lua, which is totally prototype OOP
oriented and proudly refuses to introduce any grammar construct to deal
with prototype OOP. Their tables, not unlike our dictionaries, are a
perfect abstraction of the media that should carry POOP values; or in
other words, if you had to write a POOP support, you'd need to write a
structure similar or identical to Lua tables to drive your POOP system.
So, Lua ppl said: "Hey, we got the tables to make POOP; what about
giving THEM to the users, so THEY do POOP instead of us doing it for them?"
As you can see, this approach has the disadvantage of taking some of the
control a grammar framework can grant away from the compiler; but it has
two relevant advantages of 1) making the compiler and the underlying
language support simpler (and so, more efficient) and 2) making it more
powerful and extensible.
We're in the same situation of Lua ppl, with some minor difference in
the structure of our dictionaries with respect to their table;
differences that do not affect the way POOP can be built.
Now, and here I follow your ideas, what we can do to make our POOP
support stronger is twicefold:
1) providing some poop oriented method or function for dictionaries. For
example, create a sort of inheritance declaration via
Dictionary.inherit( d1, d2 .... dn )
Even without special grammar, we may be able to perform some type
inference (when we introduce type contracts) and be able to even solve
part of this inheritances at compile time.
2) Providing language bound POOP "class" system (either in core or as a
separate module). For a reference, search for the various Lua class
libraries, which expose an "ordinary" POOP entity they call "class",
with its own inheritance schemes, and the ability to create "instances"
via a call to one of their member. Creation of an instance (entity)
includes the addition of the "base class" automatically as one of its
members.
However, whatever we do we cannot call "object" a POOP entity, mostly
for historical reasons (because of the special meaning of the keyword
"object" in Falcon), but we can use another term to name them. For
example, we may go for "entity", or if you want to sound more
"scientific", "p-entity".
About forking "self" and "this" I can't see any practical advantage, as
the context of self or this is always the same: "the thing I am
currently in". You'd just bring the same confusion you have in C (not
C++) when you can access structures by "." or "->". The only interesting
thing of having them different is that the compiler will slap you if you
mistake one for the other, and you'll have to go back and put in the
right one. And then, the semantic difference between "." and "->" is
even greater than the semantic difference between "this" object being an
"instance", a "p-entity" or an "object".
However, you know I am open to change my mind if proven wrong; we did it
several times since the beginning and we'll do it again for sure. So, if
you feel my arguments are weak, don't be afraid to bash them :-)
Bests,
Giancarlo.
Your ideas have some interesting points, which I'd like to keep into consideration; read below.
However, let me warn the audience about one very practical consideration. In my short and limited experience as a language designer, I have noticed that special syntax addressing a special problem is not always helpful as it may seem at first glance.
For example, LISP, one of the most powerful languages of all times, has nearly zero special syntax constructs, and a very simple set of basic syntax elements.
Conversely, languages having special syntax for everything tend to be rather "rigid". Often, problems resolved with special syntax constructs have been better profiled and resolved through a "function library" approach. Consider rules and constraint programming as solved by Prolog and as solved by C, Java and Python libraries. Despite the fact that the former was specifically meant to address that particular class of problems, the same problems expressed through "rules" object sets are even more readable and compact.
A still valid reason to provide special constructs may be assistance of the compiler in optimizing during compile time. However, modern approaches to compilation process has lessened this need; consider, just for instance, the special Python function "min(a,b)", which is resolved at compile time into VM opcodes and doesn't generate a real call to a function called "min".
Considering all this, the "hacking" approach to prototype OOP doesn't seem so "hacking" at all. Conversely, caging prototype OOP into a language-defined set of structures may deprive the final users of some potential that make POOP so interesting. In Falcon, Prototype OOP can be "programmed" from the inside and the outside, with the ability to dynamically change the structure of prototype "objects", (let's call them b-dict, blessed dictionaries, to avoid confusion with "instances"), without the need of special grammar to address this problem. Forcing a grammar into the current model would inevitably give off some of the potential of this paradigm, trading it off with a "clarity" which may be more a matter of personal taste than of an objective advantage.
Also, "grammarizing" the approach to prototype OOP seems to go against the modern 5th generation languages tendency which goes on the other direction. Consider the case of Lua, which is totally prototype OOP oriented and proudly refuses to introduce any grammar construct to deal with prototype OOP. Their tables, not unlike our dictionaries, are a perfect abstraction of the media that should carry POOP values; or in other words, if you had to write a POOP support, you'd need to write a structure similar or identical to Lua tables to drive your POOP system. So, Lua ppl said: "Hey, we got the tables to make POOP; what about giving THEM to the users, so THEY do POOP instead of us doing it for them?"
As you can see, this approach has the disadvantage of taking some of the control a grammar framework can grant away from the compiler; but it has two relevant advantages of 1) making the compiler and the underlying language support simpler (and so, more efficient) and 2) making it more powerful and extensible.
We're in the same situation of Lua ppl, with some minor difference in the structure of our dictionaries with respect to their table; differences that do not affect the way POOP can be built.
Now, and here I follow your ideas, what we can do to make our POOP support stronger is twicefold:
1) providing some poop oriented method or function for dictionaries. For example, create a sort of inheritance declaration via
Dictionary.inherit( d1, d2 .... dn )
Even without special grammar, we may be able to perform some type inference (when we introduce type contracts) and be able to even solve part of this inheritances at compile time.
2) Providing language bound POOP "class" system (either in core or as a separate module). For a reference, search for the various Lua class libraries, which expose an "ordinary" POOP entity they call "class", with its own inheritance schemes, and the ability to create "instances" via a call to one of their member. Creation of an instance (entity) includes the addition of the "base class" automatically as one of its members.
However, whatever we do we cannot call "object" a POOP entity, mostly for historical reasons (because of the special meaning of the keyword "object" in Falcon), but we can use another term to name them. For example, we may go for "entity", or if you want to sound more "scientific", "p-entity".
About forking "self" and "this" I can't see any practical advantage, as the context of self or this is always the same: "the thing I am currently in". You'd just bring the same confusion you have in C (not C++) when you can access structures by "." or "->". The only interesting thing of having them different is that the compiler will slap you if you mistake one for the other, and you'll have to go back and put in the right one. And then, the semantic difference between "." and "->" is even greater than the semantic difference between "this" object being an "instance", a "p-entity" or an "object".
However, you know I am open to change my mind if proven wrong; we did it several times since the beginning and we'll do it again for sure. So, if you feel my arguments are weak, don't be afraid to bash them :-)
Bests,
Giancarlo.
--
You received this message because you are subscribed to the Google Groups "FalconPL" group.
To post to this group, send email to falc...@googlegroups.com.
To unsubscribe from this group, send email to falconpl+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/falconpl?hl=en.
Gian.
I am open to suggestions
GN.