fullmoon wrote:
BYOB OOP (I just know it has a formal name...)
I don't know about that, but it does have an official slogan: "Objects are just closures." ("Closure" = a procedure including pointers to all the variables to which it refers ~~ a lexically scoped procedure.)
I get that all the references to these variables in the class's lambdas are very tightly bound, but it's difficult to explain to someone else.
That has a formal name: lexical scope. The way I think about it is this: Regardless of scope rules, when you call a procedure with inputs, you're creating a data structure (a "frame") in which the input names ("formal parameters") in the procedure definition are bound to the input values computed in the call ("actual argument values").
Now, besides those local variables, the procedure also needs access to other variables, e.g., the global ones. So the complete collection of variables accessible to this procedure call (the "environment") is actually a list of frames, starting with the one we just created in the previous paragraph, and working back to the global frame.
What does "working back" mean? That's where scope rules come in. There are basically two choices:
1. Dynamic scope. The new frame extends whatever environment was in effect at the place where the procedure was called. So every procedure call has access to its caller's variables, and its caller's caller's variables, etc., as well as its own. Examples: Logo, Smalltalk (I think), early Lisp versions. Dynamic scope is what people instinctively expect when you type variable names at the keyboard. (My Scheme students often invent it by mistake on exams.)
2. Lexical scope. When you create a procedure (in particular, with THE BLOCK or THE SCRIPT), the procedure remembers the (then-current) environment in which it was created. When that procedure is called, the new frame extends the environment that the procedure remembers. Examples: BYOB, Scheme. Lexical scope is what people instinctively expect, I think, if you refer to variables by graphically dragging a variable reference from the variable's point of definition.
This is all fully explained in SICP 3.2.
It might be best to start with an example like MAKE-ADDER
make-adder <num> report [the [< > + <num>] block]
before getting into the complexity of dispatch procedures.
EDIT: ... but the above is for you. For beginners, you probably don't want to talk about scope at all. You just want to say, people are accustomed to the idea that a variable being local to a block implies that it's temporary only because, ordinarily, the inside of the block never pokes its nose outside of the block. But once you can report a subprocedure that uses the local variable, the variable survives along with the reported procedure ("block or script").
Last edited by bharvey (2010-11-07 12:23:39)
Offline
xly wrote:
@bharvey
The application is "downloadable". Pls Read Project notes for moving of Maps and Troops.
Cool! I downloaded the one from the first (last, I guess, but first on the page) thumbnail. Now someone who understands war better than I should make the sprites actually fight each other once in position!
Offline
bharvey wrote:
It might be best to start with an example like MAKE-ADDER
Code:
make-adder <num> report [the [< > + <num>] block]before getting into the complexity of dispatch procedures.
This is a perfect example! Thanks! As an aside, I'm now writing scope rules into Haiku, should I try to implement closures?
Offline
fullmoon wrote:
should I try to implement closures?
Now, you really didn't think there was any chance I'd say no to this, did you? Closures are the first thing you should implement!
P.S. My ultimate goal for BYOB is that when a variable reference is run, first it checks lexically for the location from which you originally dragged the blob, and if that fails, then it looks up the name in the dynamic environment. Best of both worlds! I think.
P.P.S.:
(from http://xkcd.com/806/).
Last edited by bharvey (2010-11-08 01:03:24)
Offline
bharvey wrote:
Cool! I downloaded the one from the first (last, I guess, but first on the page) thumbnail. Now someone who understands war better than I should make the sprites actually fight each other once in position!
Correct, I' should think now how to implement the "rules of the game". I shall subcontract this task to my grandson, far better than me for that !
Offline
bharvey wrote:
P.P.S.:
http://cs.berkeley.edu/~bh/haiku.png
(from http://xkcd.com/806/).
Haha, yes, I know. I actually have a Haiku install in VirtualBox, and I'm trying to set up a home media server with it...it's a neat little OS. But this is my personal project and I'll name it whatever I want!
Offline
I read it all. It's funny.
Offline
bharvey wrote:
fullmoon wrote:
But this is my personal project and I'll name it whatever I want!
Of course -- I wasn't trying to dissuade you. I just think the comic is really funny. (Read the whole thing if you didn't.)
Ah, I love xkcd. This one is great, I've had similar experiences. I remember telling a Qwest techie that I was running xubuntu and he told me to load Windows and then call back.
Offline
fullmoon wrote:
he told me to load Windows and then call back.
I've had fairly good luck calling tech support late at night, for businesses that answer their phone 24/7. You're more likely to get someone who knows something.
Offline
ScratchReallyROCKS wrote:
Hey, can I help make example projects?
Yes, of course! The more the merrier. (I suppose someone might at some point propose a project so bad that we tactfully suggest rewriting it, but it hasn't happened yet.)
Offline
bharvey wrote:
ScratchReallyROCKS wrote:
Hey, can I help make example projects?
Yes, of course! The more the merrier. (I suppose someone might at some point propose a project so bad that we tactfully suggest rewriting it, but it hasn't happened yet.)
Can I help too?
________________________________
Im off to school.
Last edited by bbbeb (2010-11-09 07:43:06)
Offline
bbbeb wrote:
Can I help too?
We're delighted to have contributions from anyone. Ideally they should highlight BYOB features in nontrivial ways and should be well documented in the project notes. Extra credit if they use higher order procedures in interesting ways.
Offline
bharvey wrote:
bbbeb wrote:
Can I help too?
We're delighted to have contributions from anyone. Ideally they should highlight BYOB features in nontrivial ways and should be well documented in the project notes. Extra credit if they use higher order procedures in interesting ways.
Yay!
Offline
Hey, I want to run an idea by y'all. I was considering ways to do inheritance in Haiku, and something bharvey said about "frames" in scoping really struck me. I'm implementing variables local to procedures simply by giving the procedure-evaluator object an instance of the haiku-branded object class (hkuObject) to store its locals in. Basically the "frame" is an instance of hkuObject tacked onto an evaluator. You can access its properties, obviously, so why shouldn't you be able to access the object itself? If you could even return this 'implicit object', as I've taken to calling it, writing constructors would be a lot easier:
Point2D = |x,y|{ ^(the implicit object) //maybe @ would be an appropriate shortcut this time? I'm dying to use it :D }
I'm even writing in a shortcut, so all locals that begin with a dot (".") are packaged into the implicit object and returned automatically! So, this can actually be written:
Point2D = |.x,.y|{}
This serves to really simplify inheritance. The implicit object, like all instances of hkuObject, would have an extend() method that takes another object and adds its properties to its own. Sooooo:
Point2D_ext = |x,y|{ .extend(Point2D(x,y)) .quadrant = { ?(x >= 0 && y >= 0){ ^1} ?(x <= 0 && y >= 0){ ^2} ?(x <= 0 && y <= 0){ ^3} ^4 } } a_point = Point2D_ext(-300,500) io out(a_point quadrant()) //2
This example makes use of closures, which I haven't invented yet but I know to be possible. So does this make sense? Is it bad OOP? Should I stop bringing up topics that have nothing to do with BYOB?
Offline
fullmoon wrote:
Basically the "frame" is an instance of hkuObject tacked onto an evaluator.
One little quibble: I think you should be talking about environments, not just frames. (An environment is a list of frames; alternatively, each frame can include, besides its bindings, a parent frame (pointer), until finally you reach the global frame that doesn't have a parent.)
First class environments are definitely in accord with the general principle "first class everything"! Scheme doesn't exactly have first class environments (although recent standards have dipped their toes in the water) for efficiency reasons; if users can mess around with environments, then compilers can't assume they know about all the bindings that will exist when the program is running. But if your environment-as-object class is conservative in the ways it lets users access the bindings (e.g., not letting then add new ones) it should be okay.
Code:
Point2D = |x,y|{ ^(the implicit object) //maybe @ would be an appropriate shortcut this time? I'm dying to use it :D }
If that's the goal, first class environments may be more mechanism than you really need. I think you may be being unduly influenced by details of Smalltalk notation (or Java or something) that aren't laws of nature. There's no reason you can't just say that in your language the formal parameters to the constructor automatically make instance variables, and then just ^self, without needing to expose the environment explicitly.
In my OOP language (a Scheme macro that lets you use oop vocabulary to construct what you call "BYOB objects"), every variable mentioned in the class automatically has an accessor (but not a mutator) method. This is because for pedagogic purposes I'm more interested in ease of debugging than in information hiding.
So does this make sense? Is it bad OOP?
Let's see what Jens thinks; he's the oop guardian of this group.
Should I stop bringing up topics that have nothing to do with BYOB?
Nope! This is fun.
Offline
bharvey wrote:
fullmoon wrote:
Basically the "frame" is an instance of hkuObject tacked onto an evaluator.
One little quibble: I think you should be talking about environments, not just frames. (An environment is a list of frames; alternatively, each frame can include, besides its bindings, a parent frame (pointer), until finally you reach the global frame that doesn't have a parent.)
That is a minor quibble, but I do mean a frame. To get the entire environment, I can just do exactly what you said and work backwards recursively through the call stack and grab each frame until I find the reference I need.
If that's the goal, first class environments may be more mechanism than you really need. I think you may be being unduly influenced by details of Smalltalk notation (or Java or something) that aren't laws of nature.
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).
This is because for pedagogic purposes I'm more interested in ease of debugging than in information hiding.
Whereas I want to be able to simulate public/private namespaces. For this reason (and for the sake of the poor old Flash garbage collector), I can't just do this:
There's no reason you can't just say that in your language the formal parameters to the constructor automatically make instance variables, and then just ^self, without needing to expose the environment explicitly.
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. This is because dispatch-procedure isn't the dominant style of OOP in this language, since there's already a formal object definition...I'm implementing closures for things like callback procedures and first-class control structures, not to build objects like in BYOB.
Extended_Class = |param1,param2|{ .extend(Base_Class(param1,param2)) .anotherFunc = |param|{ //Do some stuff with a method defined in Base_Class() //I'm too tired to come up with an actual example. self baseClassFunction() //self refers to the object created by the Extended_Class function } //Look, no return at all... //But I could ^5 if I wanted to and none of the above code would have any meaning. }
Okay, I'm done discussing semantics. I'm going to finish actually writing the thing and we can talk about what it all means later. I'll put this idea of first-class environments on hold for now.
Offline
fullmoon wrote:
Posts: 1337
leet!
To get the entire environment, I can just do exactly what you said and work backwards recursively through the call stack and grab each frame until I find the reference I need.
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.
Basically in an OOP system there are two separate sources of bindings: the one having to do with procedure calls and the one having to do with object inheritance. You can have two different notations for references to these two different kinds of variables, or you can look up every reference in both hierarchies. That's what we did in Object Logo, with the rule that it's an error if the name you're using has a visible binding in both hierarchies.
But this is the best method I can come up with for inheritance without an extends keyword
Wait, I'm confused, your code example near the end of your (original) message has ".extend" in it!
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.
Offline
cool, this discussion is fun!
fullmoon wrote:
The implicit object, like all instances of hkuObject, would have an extend() method that takes another object and adds its properties to its own.
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.
bharvey wrote:
In my OOP language (a Scheme macro that lets you use oop vocabulary to construct what you call "BYOB objects"), every variable mentioned in the class automatically has an accessor (but not a mutator) method. This is because for pedagogic purposes I'm more interested in ease of debugging than in information hiding.
So does this make sense? Is it bad OOP?
Let's see what Jens thinks; he's the oop guardian of this group. smile
Hehe, no I'm not!
But, yes, I do believe that anything that "accesses" another object's "properties" is bogus OOP, even (or all the more) if it is merely for pedagogic purposes.
In my OOP objects aren't defined by their properties (or attributes or whatever you want to call their internal data) but by their behavior, i.e. by their public protocol. It's not about "information hiding" - I hate that term, it's a stupid marketing invention - but about what happens in between objects, which messages an object understands and how it responds to them. Therefore having one object "access" another object's "fields" is evil (even if it's just getting, not setting, i.e. in the case of directly invoking another object's method).
Why is it bad? Because it doesn't scale. Hardwiring objects into each other may work out for small projects, but if you're building something complex (like Scratch, BYOB, or Haiku) this eventually turns your code into a big spaghetti mess that you're going to need an army of programmers to maintain and expand over long periods of time.
How's that for today's dose of ideology?
Last edited by Jens (2010-11-10 12:24:17)
Offline
@bharvey
I'm referring to your Tutorial OOP Chapter.
1- I'm surprised that ASK returns a LIST. I took some time to fix this issue (adding Item 1 to CALL CALL...)
2- If one need to add a new property to an Object, he needs to add it "by hand" with the Block Editor, then RESET the instances (which is not the case for variables set as regular function or C-Shape Script). Then the values of properties ot modified Object are lost. I have to reset all of them.
3 - Looking at the "inheritance" method MAKE A BUZZER, is it possible to add a new property to the already existing CLASS defined by "MAKE A COUNTER" (with SET and GET methods) without using "by hand" the Block Editor and the drawback to lose existing properties. ( how to "surcharge" one pre-existing CLASS by adding it additional methods and properties ?).
Offline
Jens wrote:
Therefore having one object "access" another object's "fields" is evil (even if it's just getting, not setting, i.e. in the case of directly invoking another object's method).
Ah, but the point of providing accessors for all object variables isn't that they be used by other objects' methods; it's so that the student/programmer can interactively ask to see the values of these variables while debugging.
As for objects looking inside other objects, I think it's almost always the case that the instantiation variables (the ones whose values are provided by the caller of the constructor method) are public information -- they were generated outside the object, after all. Your argument has more weight for me with respect to the instance variables, the ones whose initial values (and whose very existence) come from inside the class definition. Maybe I should put more weight on the difference between those categories in lecture.
Offline
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.
2- If one need to add a new property to an Object, he needs to add it "by hand" with the Block Editor, then RESET the instances (which is not the case for variables set as regular function or C-Shape Script).
Yes, this and the part about inheritance are limitations of doing OOP this way. When we implement first class sprites with inheritance that may solve your problems, but we don't have a solution for you yet.
Offline