@bharvey ^^ I do think that was an appropriate 1,337th post.
bharvey wrote:
Not the call stack, not if you want lexical scope. Although these object frames are different from procedure frames anyway, so there is no "call stack" relevant to them. But also I think that means I was wrong about environments anyway.
Aha, I do see the difference between the call stack and the data structure I'm referring to. That was my stumbling block in trying to figure out closures, actually. I'll stop pretending to understand CS terminology now.
bharvey wrote:
fullmoon wrote:
Hmmm, you may be right. But this is the best method I can come up with for inheritance without an extends keyword (which doesn't make any sense in this context) or delegation (which can be quite tedious, if you ask me).
Wait, I'm confused, your code example near the end of your (original) message has ".extend" in it!
It's a method of the object primitive, not a keyword.
bharvey wrote:
fullmoon wrote:
Now we're getting into the question of what "self" should mean. I'm inclined to think that it should simply refer to the object that owns the function being called, Java-style.
Within an object constructor, doesn't that come to the same thing? But I'm not 100% sure I understand what you're saying.
No, it doesn't. Consider this constructor for an array subclass:
ArrayExt = |src|{ .extend(array new(src)) .map = |func|{ result = [] self each(|item|{ result push(func call(item)) }) ^result } }
The ArrayExt procedure is the constructor. If I allowed self to refer to the object being constructed, how could I then call the same "self" within the map function? There's no way to differentiate between constructors and plain ol' functions (which is how I want it), so the evaluator would assume that self is referring to the frame of the map function.
Jens wrote:
Good idea, I've also come to like prototypal inheritance, but I think just adding (or copying) the prototype's properties isn't what you really want. Instead you want to dynamically reference the prototype each time in case it has been changed or extended itself in the meantime, right? But that's probably what you're intending to do anyway.
No, it's not what I'm intending to do at all.
base_array = [] an_array = [5,6,7] extend(base_array) // #() = Object constructor base_array replaceAllIndicesWith = |value|{ 0 upTo(self length - 1) each(|num|{ //Iterate through each index in the array (0..2) self :(num,value) //The : function acts as both an accessor and mutator. }) ^self //Return self for convenience } another_array = [5,6,7] extend(base_array) //Now this new array has the replaceAllIndices method... io out(another_array replaceAllIndicesWith(7)) //[7,7,7] //But the first array doesn't, since it extended another_array before the latter got its new method io out(an_array replaceAllIndicesWith(7)) //undefined
I don't want an_array to magically gain the replaceAllIndicesWith method.
Last edited by fullmoon (2010-11-10 18:43:17)
Offline
fullmoon wrote:
No, it's not what I'm intending to do at all.
Code:
an_array = [5,6,7] another_array = #() extend(an_array) // #() = Object constructor an_array replaceAllIndicesWith = |value|{ 0 upTo(self length - 1) each(|num|{ //Iterate through each index in the array (0..2) self :(num,value) //The : function acts as both an accessor and mutator. }) ^self //Return self for convenience } a_third_array = [5,6,7] extend(another_array) //Now this new array has the replaceAllIndices method... io out(a_third_array replaceAllIndicesWith(7)) //[7,7,7] //But the first array doesn't, since it extended another_array before the latter got its new method io out(an_array replaceAllIndicesWith(7)) //undefinedI don't want an_array to magically gain the replaceAllIndicesWith method.
Wait, did I read that wrong or did you extend an_array with replaceAllIndices and say that another_array then has the replaceAllIndices method but an_array doesn't?
I probably just misunderstand what you're saying....
EDIT: Oh, wait, did you mean to extend another_array?
Last edited by nXIII (2010-11-10 16:42:43)
Offline
bharvey wrote:
xly wrote:
1- I'm surprised that ASK returns a LIST. I took some time to fix this issue (adding Item 1 to CALL CALL...)
I don't understand this part. ASK returns whatever the method you call returns, doesn't it? If the method returns a list, so does ASK.
1 -These methods consist in handling numbers with SET-like and GET-like instructions. Only numbers, never lists, and I'm surprised too that it returns a LIST (of one Item with one number).
2 - "we have no solution for you...". It is not a problem. I like to evaluate all the existing features and capabilities of Byob, and there are so many that there is no need for more. Again, "there is enough grain to grind for the moment".
3 - As far as OOP learning aspect is concerned my views are :
a - there are many "programming languages" far more adapted to OOP than Byob.
b - on the other hand nothing prevents a CS student to write with Byob the basic functions of OOP (it has been extensively done in the past with Lisp , even with Logo as you mentioned it (I partly did it 25 years ago and I'm far from being a CS specialist)
c - nevertheless at the end of the day a programming language which would combine the advantages of Scratch (graphic interface, drag-and-drop, parallelism, support for creativity etc), of Byob (blocks, high-class concepts) AND OOP would be a
language-killer. Is it necessary, is it reasonable ??
Offline
nXIII wrote:
fullmoon wrote:
No, it's not what I'm intending to do at all.
Code:
an_array = [5,6,7] another_array = #() extend(an_array) // #() = Object constructor an_array replaceAllIndicesWith = |value|{ 0 upTo(self length - 1) each(|num|{ //Iterate through each index in the array (0..2) self :(num,value) //The : function acts as both an accessor and mutator. }) ^self //Return self for convenience } a_third_array = [5,6,7] extend(another_array) //Now this new array has the replaceAllIndices method... io out(a_third_array replaceAllIndicesWith(7)) //[7,7,7] //But the first array doesn't, since it extended another_array before the latter got its new method io out(an_array replaceAllIndicesWith(7)) //undefinedI don't want an_array to magically gain the replaceAllIndicesWith method.
Wait, did I read that wrong or did you extend an_array with replaceAllIndices and say that another_array then has the replaceAllIndices method but an_array doesn't?
I probably just misunderstand what you're saying....
EDIT: Oh, wait, did you mean to extend another_array?
*Facepalm*
Nice catch, I was mixing up my variable names.
Offline
fullmoon wrote:
If I allowed self to refer to the object being constructed, how could I then call the same "self" within the map function? There's no way to differentiate between constructors and plain ol' functions (which is how I want it), so the evaluator would assume that self is referring to the frame of the map function.
Hmmm. You've repeatedly distinguished what you're trying to do from dispatch-procedure-based oop as in BYOB. So, an object is something different from a procedure. But every procedure call makes an object?
I think you're still trying to merge the procedure call hierarchy and the object inheritance hierarchy in a way that isn't fully thought out. But I'm not 100% sure that it's not I not fully thinking out, because I'm not 100% sure I understand your vocabulary.
In Those Other Languages, a constructor is very different from an ordinary procedure, even if not syntactically, because the constructor leaves behind a persistent data structure, and the other procedures don't. It's only in "BYOB style" oop that objects are procedures, and even then they're not the procedure that created them, but rather a lambda inside that procedure. In a lexically scoped language with lambda, every procedure call leaves around potentially persistent data. There is no "call stack" but there's a call heap, and objects essentially are environments in the heap, although technically they're a procedure defined within that environment.
So, because I see constructors as being semantically different from method procedures, I have no problem about the antecedent of self in your map example.
Does that make sense, or are we still speaking at cross purposes?
Offline
fullmoon wrote:
I don't want an_array to magically gain the replaceAllIndicesWith method.
Why not? If another_array is a kind of an_array, it should behave like an an_array, however arrays behave, unless it specifically shadows a method.
If, by contrast, replaceAllIndicesWith (have I mentioned that I hate camel-casing?) is a way that only a certain subset of arrays should behave, then it should have been defined in another extension/subclass of array, rather than in array itself.
(Your example is a little weird anyway, in that you are extending an object for the sake of its data rather than for the sake of its methods. More typically, a child of a prototype will have local variables with the same names but different values, but will inherit the actual methods, not just their names.)
Offline
bharvey wrote:
Hmmm. You've repeatedly distinguished what you're trying to do from dispatch-procedure-based oop as in BYOB. So, an object is something different from a procedure. But every procedure call makes an object?
Oh, heavens no. That would be absolute madness. If you want to write a procedure that returns nothing, that's absolutely fine. Just don't dot any of the local variables and nothing (well, null) will be returned.
So, because I see constructors as being semantically different from method procedures, I have no problem about the antecedent of self in your map example.
AHA! Yes! Differentiating between constructors and plain ol' procedures. Javascript does this very nicely; you can write constructors as normal functions and then call them with a preceding 'new' keyword, and all the references to 'this' inside the function reference the object being constructed. Check it out. But I think that once again the system I have works fine. I've thrown out the idea of truly first-class environments; let's see what's left:
- Constructors are syntactically and semantically identical to functions and are called the same way.
- If you want the function to return something other than the implicit object (and most functions will) then use the ^ keyword.
- "self" and the dot prefix are identical. Inside any function, these will reference the object under construction. Therefore nesting constructors is not just stupid, it's impossible.
- If you want to return nothing, don't use self or the dot.
- To access an object outside of the scope of a function, make sure it's already defined (mentioned) somewhere higher up.
- To use local variables that aren't automatically returned, don't prefix them with dot or self -- just make sure there isn't another definition accessible from the function with the same name.
- And finally, if you want to definitively scope a reference to the currently executing function, prefix it with @. @ is actually a reference to the current function, so you can recurse with "@ call()"!
bharvey wrote:
Does that make sense, or are we still speaking at cross purposes?
That definitely makes sense. I'm just being stubborn in my belief that constructors and other functions can be identical. I'm excited, though -- what I'm saying finally makes sense to me, and possibly to others!
Thanks so much for your help!
Offline
bharvey wrote:
fullmoon wrote:
I don't want an_array to magically gain the replaceAllIndicesWith method.
Why not? If another_array is a kind of an_array, it should behave like an an_array, however arrays behave, unless it specifically shadows a method.
I'll have to give this more thought...
Offline
fullmoon wrote:
- Constructors are syntactically and semantically identical to functions and are called the same way.
- If you want the function to return something other than the implicit object (and most functions will) then use the ^ keyword.
- "self" and the dot prefix are identical. Inside any function, these will reference the object under construction. Therefore nesting constructors is not just stupid, it's impossible.
- If you want to return nothing, don't use self or the dot.
- To access an object outside of the scope of a function, make sure it's already defined (mentioned) somewhere higher up.
- To use local variables that aren't automatically returned, don't prefix them with dot or self -- just make sure there isn't another definition accessible from the function with the same name.
- And finally, if you want to definitively scope a reference to the currently executing function, prefix it with @. @ is actually a reference to the current function, so you can recurse with "@ call()"!
What about having @ and self be the same object?
That way, all a constructor really does is extend itself and return the extended version (self from inside the function). I guess it would really be a new copy of itself, just so it could be in the right stack and not actually modify the contructor. This would only happend when a function is called with the new keyword, allowing for nested contructors and methods referring to self in the same object. For example:
car = |color|{ self color = color self paint = |newColor|{ self color = newColor } ^ self } myCar = new car('blue') myCar paint('red') //myCar color = red myPaintedObject = new (myCar paint)('green') //myPaintedObject color = green, myCar color = red brokenCar = new myCar paint('orange') //impossible, this is calling "new" on a null object (not a function object) //like typing new (myCar paint('orange'))
Last edited by nXIII (2010-11-10 20:37:34)
Offline
fullmoon wrote:
nXIII wrote:
What about having @ and self be the same object?
Mmmmm...possibly. This is really tempting (it's also how JS does it, after all). I'm just hesitant to start using the 'new' keyword, otherwise I'd take your suggestion in a heartbeat.
True...
I like the way JS does things. My only complaint is that the closure keyword is too long
Offline
nXIII wrote:
fullmoon wrote:
nXIII wrote:
What about having @ and self be the same object?
Mmmmm...possibly. This is really tempting (it's also how JS does it, after all). I'm just hesitant to start using the 'new' keyword, otherwise I'd take your suggestion in a heartbeat.
True...
I like the way JS does things. My only complaint is that the closure keyword is too long
Haha, I do believe that I pointed that out about ten pages ago on this thread!
It looks like I'm going to have to chose between purity and convenience...
If I keep the simplicity of constructors as regular functions, I lose the flexibility your suggestion offers.
Who would have thought designing a language would be so tough?
Offline
fullmoon wrote:
- To use local variables that aren't automatically returned, don't prefix them with dot or self -- just make sure there isn't another definition accessible from the function with the same name.
This seems unnecessarily restrictive. If a local variable is declared within this very procedure, then the programmer's intent is perfectly clear, and it shouldn't matter if there are other bindings for this name that would otherwise be visible here.
The problematic case is the one in which the variable isn't local to this procedure, so we have to look outward to find out what the programmer meant, and we find both an accessible binding in a procedure (a procedure whose definition encloses the definition of this one, in a lexically scoped language, or a procedure that directly or indirectly called this one, in dynamic scope) and a binding belonging to the current object (to "self"). Neither of these obviously outranks the other. (If one of the competitors is a global binding, then the other one wins.)
This problem case may not be possible if the language is lexically scoped and if all method definitions are lexically inside (not merely linked to) the class definition. In that case, object variables are lexically further away than enclosing method definitions, so the latter win. Although if the method can be called from outside the object (and if not, what good are they) then it can't straightforwardly be considered lexically inside the class definition, it seems to me. (In Java, methods marked "public" have this status.) I may be confused on that last point.
EDIT: Oh, I see, the issue is that you don't declare local variables, you just use them and expect them to be created automagically. But there are lots of cases in which some word like "list" is used as a name in many different procedures, and it's too hard to keep track of all of them, especially if more than one programmer are collaborating on a program.
Last edited by bharvey (2010-11-11 01:36:18)
Offline
nXIII wrote:
Code:
car = |color|{ self color = color self paint = |newColor|{ self color = newColor } ^ self } myPaintedObject = new (myCar paint)('green')
This usage feels a little strange to me. For one thing, it depends too much on looking inside the car class to be sure that the paint method sets an object variable, rather than, say, some piece of a global list structure or something. So Jens won't like it.
But also, if you just read the car class definition, then the paint method makes perfect sense as something in the context of a car. Saying that more from an implementor's standpoint, "self" inside the paint method makes sense lexically; I can read the class definition and I know which "self" this reference means -- the car whose method this is! But you're making "self" something that's left hanging until runtime, when the paint method definition is sort of injected into whatever context I'm in right now, including no context at all in the MyPaintedObject (sorry, I just can't do it in camel) example. It's as if the paint method were a C #define textual macro.
It may be that you two can specify a feature like this in a way that's internally consistent, but I'm not sure that it'll match anyone else's intuitions.
Sorry, I'm not trying to rain on your parade, you should definitely try to push this idea as far as you can and see what you end up with, and then post some actual working programs for the rest of us to critique.
Offline
fullmoon wrote:
Just don't dot any of the local variables and nothing (well, null) will be returned.
Wait, so, I'm not sure why I would, but I can't create an object class that doesn't have any variables or methods? Or, if I don't dot anything, is an object created but just not returned?
Btw, I'm having trouble understanding how the use of the word "prototype" in the feature later in that tutorial page connects to what I think of as prototyping: the kind of OOP system in which there is no class/instance distinction, but rather, anything can be instantiated or subclassed.
Last edited by bharvey (2010-11-11 01:32:41)
Offline
bharvey wrote:
But there are lots of cases in which some word like "list" is used as a name in many different procedures, and it's too hard to keep track of all of them, especially if more than one programmer are collaborating on a program.
And thus we have the @ option, which forces a reference to refer to the local vars of the current function:
phone_number = 8675309 its_an_emergency = { @ phone_number = 911 phone dial(phone_number) }
I'm hovering between making @ a keyword like let, dim, or var or just allowing it to be an object, which brings us back to the original issue of first-class environments. Either way, it only has to be used once, since the evaluator will pick up on the new definition for every subsequent use. I think this solves the problem you brought up.
Offline
bharvey wrote:
fullmoon wrote:
Just don't dot any of the local variables and nothing (well, null) will be returned.
Wait, so, I'm not sure why I would, but I can't create an object class that doesn't have any variables or methods? Or, if I don't dot anything, is an object created but just not returned?
Sure, just return a new, empty object. There are about seven ways to do this currently, but the simplest is:
ObjectPlease = { ^#() }
The hash is just a shortcut for the top-level object function that simply creates a new object. If you don't dot anything, no object is ever created.
bharvey wrote:
Btw, I'm having trouble understanding how the use of the word "prototype" in the feature later in that tutorial page connects to what I think of as prototyping: the kind of OOP system in which there is no class/instance distinction, but rather, anything can be instantiated or subclassed.
Are you referring to the section where they talk about cat.prototype?
Offline
bharvey wrote:
But also, if you just read the car class definition, then the paint method makes perfect sense as something in the context of a car. Saying that more from an implementor's standpoint, "self" inside the paint method makes sense lexically; I can read the class definition and I know which "self" this reference means -- the car whose method this is! But you're making "self" something that's left hanging until runtime, when the paint method definition is sort of injected into whatever context I'm in right now, including no context at all in the MyPaintedObject (sorry, I just can't do it in camel) example. It's as if the paint method were a C #define textual macro.
It may be that you two can specify a feature like this in a way that's internally consistent, but I'm not sure that it'll match anyone else's intuitions.
Sorry, my example was kind of bad because there's no reason you'd ever want to do what I did, but it showed what exactly was being done. Here's a JS-itized version:
var Car = function(color){ this.color = color; this.paint = function(newColor){ this.color = newColor; } this.abc = function(){ this.a = 2; this.b = 5; this.c = 100; } var foo = 0; this.referencedVar = function() { this.bar = ++foo; } } var myCar = new Car('blue'); myCar.paint('red'); alert('myCar.color = ' + myCar.color); //red var myABC = new (myCar.abc)(); alert('myABC.a = ' + myABC.a); //2 alert('myCar.a = ' + myCar.a); //undefined var myPaint = new (myCar.paint)('yellow'); alert('myPaint.color = ' + myPaint.color); //yellow alert('myCar.color = ' + myCar.color); //red
This just shows that, depending on the usage of the "new" keyword, the methods can refer to either the object on which the method is being called or the object being created, which is what I was trying to say.
EDIT: Wow, like 3 pixels too long for the code box
Last edited by nXIII (2010-11-11 11:32:27)
Offline
AGH! We completely forgot to point out the 2000th post! I guess we'll just have to wait for 3000.
Offline
nXIII wrote:
@bharvey, I made you a new signature! Like it?
http://i54.tinypic.com/11bp106.jpg
YOUR IMAGE IS MINE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Great job.
(That reminds me. I need to make a Fluxbyte one!)
Last edited by shadow_7283 (2010-11-11 17:22:39)
Offline
shadow_7283 wrote:
(That reminds me. I need to make a Fluxbyte one!)
Just remembered to finish the ASCII art
EDIT: ugh...that doesn't look too good. Lemme fix it.
EDIT 2: Here:
Last edited by nXIII (2010-11-11 20:00:19)
Offline
nXIII wrote:
shadow_7283 wrote:
(That reminds me. I need to make a Fluxbyte one!)
Just remembered to finish the ASCII art
EDIT: ugh...that doesn't look too good. Lemme fix it.
EDIT 2: Here:
http://i55.tinypic.com/261owh4.jpg
What's FluxByte?
Offline
fullmoon wrote:
nXIII wrote:
shadow_7283 wrote:
(That reminds me. I need to make a Fluxbyte one!)
Just remembered to finish the ASCII art
EDIT: ugh...that doesn't look too good. Lemme fix it.
EDIT 2: Here:
http://i55.tinypic.com/261owh4.jpgWhat's FluxByte?
We're getting really off-topic, sorry....
Offline
nXIII wrote:
fullmoon wrote:
nXIII wrote:
Just remembered to finish the ASCII art
EDIT: ugh...that doesn't look too good. Lemme fix it.
EDIT 2: Here:
http://i55.tinypic.com/261owh4.jpgWhat's FluxByte?
We're getting really off-topic, sorry....
No, I genuinely want to know!
(Don't worry...I've singlehandedly kept this thread off topic for days.)
Offline