Comments on: Concluding on object creation (copy and bind)
Thank you for your comments on earlier articles related to object creation, #239 and #212. (I apologize for repeating myself.)
I've spent the last few days reading your comments and thinking about the issues. Objects are used for a lot of things in REBOL: for structures of raw data, environments of inter-bound data, namespaces such as modules, and prototype-based objects with accessor functions. So, we need many different behaviors from object creation, and no single approach will provide them all.
After reading your comments, here's what I've concluded can be done right away for A80:
- Keep the default MAKE compatible with R2. So, we will not copy sub-objects. This just makes porting easier, and we do want developers moving to R3.
- Change COPY to do a shallow copy of objects. This will be very fast, with no deep copying and no binding. It let's you make a clone of an object with the same references.
- Let COPY/deep deep copy all object fields of all types. Everything is copied, but no binding is done. (Use bind for that.)
Then, I think we should consider these additions for A81:
- Provide a new refinement to COPY that accepts a typeset to indicate what is to be copied. For example, if you want to copy only strings, you specify STRING!. To copy all block types, you'd use the ANY-BLOCK! typeset. (Suggest name of the refinement.)
- Add a spec block to objects. This is a block similar to the spec block of functions or the header blocks of modules. It gives us a way to embed documentation into objects (to show up on HELP), specify a creation init function (that runs as part of MAKE), and specify a typeset for what to copy on MAKE.
The general form of object creation would be:
make object! [spec body]
Note that the form is similar to the low level function creator:
make function! [spec body]
where spec and body are both blocks.
Of course, the classic form would still be accepted; you can still write:
obj0: make object! [
var2: [a block here]
But, if you need more control or want embedded-docs, you write:
obj1: make object! [
title: "Example object"
var2: [a block here]
This will deep copy all series datatypes, rebind all block-related types, and call the internal my-init function at the end of the MAKE.
We could also define a helper function:
object: func [spec body] [
make object! reduce [spec body]
Note that if you don't specify the copy option, then the default copy method occurs (R2-defined.) If you give it a NONE, then no values are copied (a shallow object copy.)
Also, it should be pointed out that the object's spec would be referenced by all derived objects. If you write:
obj2: make obj1 [var3: 123]
the original spec still applies and it is also referenced from obj2. (This might have some implications for MOLD.)
There are a few other options that might make sense.
I'd suggested an additional refinement to copy a while ago on AltME - /types or /datatypes, accepts datatype! or block! as argument (Ex: [block! object!] and not string! or function!)|
hum ... and how will we know when our variable is countaning a reference or a direct link to full "cloned" thing ?
i would apreciate a is-pointer? or is-reference? kind.
Actually in rebol 2 that's already a pain to figure out using copy who is who... and that have many insidence on memory use obtimisation for example.
a good example of rebol code using copy extensive way from many rebol technologies (parse etc...) is viva-rebol.r. If you can spend some minutes to read it you will see it's pretty deficult to see in the code when something is a clone or when it's a reference.
copy is linked to freing object too and many other issues
in rebol way to handle pointers/clonage that really bother me.
i dream about a remove-all-ref and a free-objet functions
sorry I'm not really following what is in R3 in that topic and maybe that kind of evolutions are made in R3.
think of it carl not knowing what you have "really" in your variables is not a way to keep the code obtimised and using low memory. and not being able to really remove from the memory stack the data when you decide to is not a good way to work neither.
I rod you speaking about database extension in another post but really do you think a database collector made in rebol can work 24H a day 7D a week without having a close control on what is in rebol memory stack at anytime ?
do you think billions of database requests and side var storage can affort to remain endlessly in memory when not needed waiting for the garbage collector to consider freeing them ?
close control of memory is what any programer is working after most of his carreer. Having the things needed at the right time and then removing them is a basis in our industry. (but ofcourse initially rebol VM cgi thing was thinked as start run do the tricks and stop so the os woud clear the memory used and do the task since it was planned for small time execution it was logical to have that kind of I don't care what i do with the memory phylosophy since the rebol process it self was planed to work for a small amount of time (cgi use for example) but thing is Carl we love rebol so much that we want to start a rebol process and never end it ^__^ (project like viva-rebol or cheyenne are made of that wood )
shadwolf: "and how will we know when our variable is countaning a reference or a direct link to full "cloned" thing ?" - Your variable is *always* just referencing, except for the case when the value is of an IMMEDIATE! datatype.|
...and, to not confuse readers, even in case the value is of IMMEDIATE! datatype, you can wery well assume it is still just referenced, since this is just an implementation detail not influencing the behaviour in any way, except for the fact, that IMMEDIATE! values cannot be made mutable.|
Shadwolf: The only info you can get is whether two variables reference two values or just one. This can be found out using the SAME? function and similar approaches, see also the IDENTITY article.|
Shadwolf: "do you think billions of database requests and side var storage can affort to remain endlessly in memory when not needed waiting for the garbage collector to consider freeing them ? " - definitely!|
More ambitious than I was hoping for - looks good, and that spec block shows some potential. Would the COPY option apply to any-block! types as well as object!, for copying contents?|
The new definition of the function OBJECT, namely |
func [spec body] [make object! reduce [spec body]] is not backwards compatible with the old definition
func [blk][ make object! append blk none] that was a slight extension of the original CONTEXT function. For simplicity I propose to drop the APPEND part and either abolish CONTEXT or make it a synonym of OBJECT. The variant with a single block argument should still be allowed.
the original spec still applies and it is also referenced from obj2. (This might have some implications for MOLD.)
so the spec kind of acts like a class reference in class based object oriented systems... maybe i would be tempted to use it for something like that :/
can we mix class based and prototype based OO somehow?
iirc i saw some implementation of class based behavior over the prototype based one...
(actually i did some "type" tracking in a form handling application of mine where u can create descendants of field groups with all their validations and display methods)
Meijeru, OBJECT was a new function in R3, so there is no need for backwards compatibility. It would be a good idea to keep CONTEXT as it is (since it existed in R2), and it could serve as the single block argument variant you mention.
OneTom, the spec acts like metadata, not a class. As with all prototype-based OO systems, classes are emulated when needed.
I'm sorry to see all these complexities arising about easy things like copying.
Let me say just this:
Carl is forgetting his own philosophy and now struggling with the desastrous consequences of the
basically wrong concept of OOP.
Since decades I strictly avoid "objects".
To address array cells by mnemonics #DEFINES are normally used. OOP is not needed for that. The rest of OOP is just nonsens. Professional developers do not want and use these - sorry childish - things.
Yes, I'm working globally with arrays and functions only. Never missed and never will use anything else.
What is missing in REBOL is a clearly defined function syntax. The scope of variables could be better defined. By default all variables should be local. Each script should be treated as function, too.
Except REBOLs marvelous functions that's (almost) all serious developers need. ;-)
btw: Why do we have a DATATYPE! "char" (#"x")? All strings are characters - longer, shorter or just one. There arise many problems from that: For example LIT-WORD! cannot be used on #"x". Isn't that really surprising? Printable or not: a string is a sring (except ascii 0 of course).
RAtio got my vote..
Ladyslav i'm sorry but you don't prove in any ways that i'm wrong on the contrary ... All is reference i'm not agree the data the real one is loaded in memory and removing from memory is almost close to the impossible.
Ladyslav to all your clever arguments i can opose even clever ones to show that this kind of data management i a mess.
And in VID area that such the case that someone like ashley wanted to optimise VID had to rebuild all the objects in his custom VID version called RebGUI i don't know if you remember it ?
And don't tell me VID is out of the topic ... VID is all about object and show you perfectly the difficulty to know
when your var contains a reference or just the object
lets take an example concrete i'm in viD i create a list of many sub widgets to be show into a box/pan alternatively ( look rekini irc client source code for example) in order to show the right widget in the box/pan at the right time. (when the use choose it) now if the use choose to "close" one of those widget what happend ?
I remove from the list of the available sub widget the référence to that sub widget making so artificially the user unable to call that closed visual area again. But concretly this "visual area" or subwidget wasn't removed from memory and will stay into it until the user choose to end the rebol.exe process (call to rebol quit instruction).
is it normal ?
my answer is no. that's plain and simple.
Ratio: "Since decades I strictly avoid objects" - you are welcome to do as you wish. I see it only as a *fraction* of possible options.|
Well... I did not expect to see so many detailed comments (but I think I liked Maxim's comment the best.)
Some replies to various comments:
Izkata: Well.. it is just a matter of time, and your suggestions are implemented.
Shadwolf and Ratio: Good comments, and in general, I agree. I should do more blogs on the related topics. However, there are a few things worth mentioning:
- In R3, to determine if two separate values share the same data... use the SAME? function (as Ladislav noted.) This will do what you need. It tells you if the "pointers" are to the same string, binary, block, etc.
- Regarding application up-time: I would make a claim that some REBOL processes are perhaps the longest running of any. Many of our IOS, AltME, and other REBOL processes that handle databases, some with of thousands of connections per hour, run for many years in the same process without running out of memory! The REBOL model for programs works quite well.
- The concept of objects in REBOL is different from other languages, and it is useful... even if you do dislike or not accept OOL. (In fact you are using REBOL objects even though you don't realize it! All contexts (even your global variables and functions) are actually objects!)
- You mention that variables should be local for functions. In R3, that's why we created the FUNCT function (helper). Try it out.
- In your VID example, if you removed all references to the widget, then it will eventually be removed from memory. If that did not happen, then many programs like AltME would not work properly.
BrianH: yes (on COPY for any-block! types.)
I've added a page to DocBase to describe some of the fine points about COPY:
There are a few small issues. For example, should we remove the /deep refinement to TAKE? Why? Because its rarely used and it duplicates COPY/deep. You can always write:
copy/deep take series
if you need it.
We don't offer (and rightly so) an APPEND/deep or INSERT/deep to do a copy of their sub-values.
A80 has been released with the stated changes. Type CHANGES at the R3 prompt for details.|
I note that you have introduced into A80 one of the changes foreseen for A81: the /types refinement on copy.
The other one (adding a spec block for objects) is still for A81 or later, I presume. As discussed with BrianH, it would be nice, at that time, to have helper functions |
object: func [spec body] [make object! reduce [spec body]] and
context: func [blk][make object! reduce [none blk]]
Carl, with OOP ("object orientated programming") I mean the strange concept to "attach" functions to variables.
If e.g. we have the variable "curdir" returned by WHAT-DIR an OOP fan would "attach" a part or all functions managing directories to this variable. If he uses another directory he has to do the same again and so on. They call it sometimes "inherit" and chat about "classes".
So a "horse" would be assigned functions like feeding, selling, buying..., a "car" the functions driving, selling, buying, reparing... etc. If they have different horses or cars all must be done for each.
I think this is such an odd, idiotic concept that I often wondered why it is so widespread.
As far I can see in REBOL we (mostly?) have a "reduced" understanding of "objects" (just arrays, addressable not by index but by the names we assigned for the columns).
This obj/name syntax is quite ok for me. Though not really needed (could be done by a proprocessor translating #defines) it's easier to use esp. while debugging.
It's fine to have "objects" in REBOL system. It helps a lot to understand what's going on.
personally I cannot use this special kind of arrays because they are not addressable via index (pick etc).
So instead of using generic functions we would have to write separate functions for each different array or copy the complete "object" to a "normal" array before. Or does there exist any other way to handle REBOL "objects" generically?
meijeru: yes, the change to objects required a fundamental change to COPY... so it was good timing for COPY/types to be added.
Ratio: Prompted by your comments, I have posted a new article regarding OOL, and about my view of it in REBOL. See the article at The problem with OOL is not OO.|
What about the idea of protecting values. This way they won't be copied, when creating a new object. Today we can protect words, but not values. So if a word is used for an indirect value, we can kinda change the word anyway (I say 'kinda', because we change the value, the word points to):
>> a: "a string"
>> b: a
>> protect 'a
>> change b "hmm"
My idea is, that if we were able to protect values producing constants, this idea could be used to guide the creation of objects. And Copy Semantics could maybe be a lot simpler.
John, in R3 you can protect values already. In your above example you were protecting the word 'a, not the value assigned to it. Try protect a instead.|
This document is up-to-date for R3:
I see. And the idea to use this to guide copying of content within objects?|
Ratio: Prompted by your comments, I have posted a new article regarding OOL, and about my view of it in REBOL. See the article at The problem with OOL is not OO.
Thank you, Carl. I (almost) 100% agree.
My answer is posted at the same location.
Post a Comment:
You can post a comment here. Keep it on-topic.