My other message today reveals that I've still been unable to get Self running under Cygwin. So here are a couple of questions about Self the answers to which would help me with Caper's semantics.
(1) Is there any use for an inline object-with-code? It seems to me that there isn't, because there is no selector associated with such an object, and therefore it's rather difficult to invoke the code.
(2) Conversely, what happens if a keyword slot contains an object- without-code? I assume that accessing the object in such a slot by sending a keyword message to its container would return (a copy of) the object itself with the parameter slots filled in. Is that right?
I'm trying to work out what the deep meaning of Self's objects-with- and-without-code is (cf Smalltalk's separation of methods and instance variables into two namespaces), in order to find the best way for Caper to distinguish between "values" (evaled at definition) and "methods" (evaled at invocation) in fields.
Thanks to anyone who can help.
Cheers, Paul
On Tuesday 18 November 2003 11:35, Paul Chapman wrote:
My other message today reveals that I've still been unable to get Self running under Cygwin.
I have never tried it, so I can't help you there. Personally, I would just boot some CD-based Linux (such as Knoppix) and use that to try out Self (which could be on the Windows partition on the hd).
So here are a couple of questions about Self the answers to which would help me with Caper's semantics.
(1) Is there any use for an inline object-with-code? It seems to me that there isn't, because there is no selector associated with such an object, and therefore it's rather difficult to invoke the code.
This was a feature in Self 1.0 that was replaced by a parser optimization in later versions. So it used to be that if you wrote
3 + (4 * 5)
and looked at the bytecodes, you would see what you call an "object-with-code" as the argument for the '+' message. There is no selector, but just pushing it to the stack was enough to invoke it. Something like
3 + [4 * 5] value
Since these were full method objects, you could write
3 + (|z| z: 4 * 5. 10 + z)
which in a more recent version of Self will give you an "inner methods are no longer supported" error.
(2) Conversely, what happens if a keyword slot contains an object- without-code?
That gives you a syntax error. "Empty objects" (even if they have slots!) can only live in unary slots.
I assume that accessing the object in such a slot by sending a keyword message to its container would return (a copy of) the object itself with the parameter slots filled in. Is that right?
That is not how the current implementation works, but is a very reasonable thing to expect. In fact, there are good arguments for both returning the object itself and for returning a copy of it.
I'm trying to work out what the deep meaning of Self's objects-with- and-without-code is (cf Smalltalk's separation of methods and instance variables into two namespaces), in order to find the best way for Caper to distinguish between "values" (evaled at definition) and "methods" (evaled at invocation) in fields.
You might also find you need something like blocks, which are a huge complication.
-- Jecel
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr <jecel@m...> wrote:
On Tuesday 18 November 2003 11:35, Paul Chapman wrote:
My other message today reveals that I've still been unable to get Self running under Cygwin.
I have never tried it, so I can't help you there. Personally, I
would
just boot some CD-based Linux (such as Knoppix) and use that to try
out
Self (which could be on the Windows partition on the hd).
So here are a couple of questions about Self the answers to which would help me with Caper's semantics.
(1) Is there any use for an inline object-with-code? It seems to
me
that there isn't, because there is no selector associated with
such
an object, and therefore it's rather difficult to invoke the code.
This was a feature in Self 1.0 that was replaced by a parser optimization in later versions. So it used to be that if you wrote
3 + (4 * 5)
and looked at the bytecodes, you would see what you call an "object-with-code" as the argument for the '+' message. There is no selector, but just pushing it to the stack was enough to invoke it. Something like
3 + [4 * 5] value
Since these were full method objects, you could write
3 + (|z| z: 4 * 5. 10 + z)
which in a more recent version of Self will give you an "inner
methods
are no longer supported" error.
(2) Conversely, what happens if a keyword slot contains an object- without-code?
That gives you a syntax error. "Empty objects" (even if they have slots!) can only live in unary slots.
I assume that accessing the object in such a slot by sending a keyword message to its container would return (a copy
of)
the object itself with the parameter slots filled in. Is that
right?
That is not how the current implementation works, but is a very reasonable thing to expect. In fact, there are good arguments for
both
returning the object itself and for returning a copy of it.
I'm trying to work out what the deep meaning of Self's objects-
with-
and-without-code is (cf Smalltalk's separation of methods and instance variables into two namespaces), in order to find the best way for Caper to distinguish between "values" (evaled at
definition)
and "methods" (evaled at invocation) in fields.
You might also find you need something like blocks, which are a
huge
complication.
A huge complication? Aren't block closure's just anonymous methods (which are objects) with their proto set to their enclosing environment? I thought it was something like this in self.
-- Mike
On Tuesday 18 November 2003 21:48, Mike Austin wrote:
A huge complication? Aren't block closure's just anonymous methods (which are objects) with their proto set to their enclosing environment? I thought it was something like this in self.
It does look simple when you put it that way.
But what is "their enclosing environment"?
A) The method object in which the block literal is stored? B) The closure/context/activation object cloned from A in which the "push literal" bytecode is actually executed?
And who are we talking about in "their proto (parent, actually) set to"?
1) The block literal object? 2) The result of referencing 1 with a "push literal"? 3) The block closure resulting from sending 'value' to 2?
In Self, we set the parent of 3 to point to B and that is far more complicated to arrange than I would like. Sadly, simplifying this normally leads to a loss of important functionality (like being able to send 'value' more than once to one of your block arguments or recursive invocation of blocks).
-- Jecel
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr <jecel@m...> wrote:
On Tuesday 18 November 2003 21:48, Mike Austin wrote:
A huge complication? Aren't block closure's just anonymous
methods
(which are objects) with their proto set to their enclosing environment? I thought it was something like this in self.
It does look simple when you put it that way.
But what is "their enclosing environment"?
A) The method object in which the block literal is stored? B) The closure/context/activation object cloned from A in which
the
"push literal" bytecode is actually executed?
B) The activation object, or method-instance for another term
And who are we talking about in "their proto (parent, actually) set
to"?
- The block literal object?
- The result of referencing 1 with a "push literal"?
- The block closure resulting from sending 'value' to 2?
1) The block literal object
In Self, we set the parent of 3 to point to B and that is far more complicated to arrange than I would like. Sadly, simplifying this normally leads to a loss of important functionality (like being
able to
send 'value' more than once to one of your block arguments or
recursive
invocation of blocks).
Although Io handles locals a bit different, it is essentially the same idea when I do this:
myObject = Object clone do ( ` add = method ( n, ` ` return block( x, return n + x ) ` ) )
a = myObject add( 10 ) b = myObject add( 20 )
write( a( 10 ), "\n" ) write( b( 10 ), "\n" )
io closure_test.io
20 30
The returned block's proto is set to the new "Locals" instance that is created when calling add. It works simply, and beautifully.
-- Mike
On Thursday 20 November 2003 07:17, Mike Austin wrote:
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr <jecel@m...>
wrote:
But what is "their enclosing environment"?
B) The activation object, or method-instance for another term
Good.
And who are we talking about in "their proto (parent, actually) set
to"?
- The block literal object?
- The result of referencing 1 with a "push literal"?
- The block closure resulting from sending 'value' to 2?
- The block literal object
Now we have a problem. Unfortunately, B doesn't yet exist when the parser is creating 1 from the source text. It is likely that you have different definitions from what I am used to, so I will try to be clearer below.
Although Io handles locals a bit different, it is essentially the same idea when I do this:
myObject = Object clone do ( ` add = method ( n, ` ` return block( x, return n + x )
The expression "block(...)" is what I called 1 above.
` ) )
a = myObject add( 10 ) b = myObject add( 20 )
a and b are two instances of what I called 2 above.
write( a( 10 ), "\n" ) write( b( 10 ), "\n" )
a( 10 ) and b( 10 ) are two instances of what I called 3 above.
io closure_test.io
20 30
The returned block's proto is set to the new "Locals" instance that is created when calling add. It works simply, and beautifully.
Ok, so you set the proto of 2 to B and then 3 gets to inherit it. I haven't paid attention to such details in Io, however, and would be happy for you or Steve to correct me.
It certainly does work, but I wouldn't call it simple. Since Self doesn't allow non-lifo blocks, I'll rewrite your example in Smalltalk instead:
MyObject>>add: n ^ [:x | x + n]
a := myObject add: 10. b := myObject add: 20.
Transcript show: (a value: 10). Transcript show: (b value: 10).
I will agree that if you don't care about the multiple steps and objects needed to make this work, it looks simple enough. Explaining how to use blocks isn't a problem for me, but explaining how they get their job done is.
-- Jecel
On Nov 20, 2003, at 12:35 PM, Jecel Assumpcao Jr wrote:
It certainly does work, but I wouldn't call it simple. Since Self doesn't allow non-lifo blocks, I'll rewrite your example in Smalltalk instead:
MyObject>>add: n ^ [:x | x + n]
a := myObject add: 10. b := myObject add: 20.
Transcript show: (a value: 10). Transcript show: (b value: 10).
I don't understand why this is simpler. Could you explain? It's not important, I'm just confused and would prefer not to be. :-)
Here's a simplified Io version:
add = method(n, block(x, x + n)) a = add(10) b = add(20) a(10) print b(10) print
-- Steve
On Thursday 20 November 2003 19:30, Steve Dekorte wrote:
On Nov 20, 2003, at 12:35 PM, Jecel Assumpcao Jr wrote:
[I'll rewrite your example in Smalltalk]
I don't understand why this is simpler. Could you explain? It's not important, I'm just confused and would prefer not to be. :-)
Not simpler - it looks almost exactly the same. Yet under the covers there is all the complexity I was talking about with four or five different objects (which aren't obvious from reading the source).
My wild guess was that Io might also not be that simple when seen from the perspective I was talking about.
Here's a simplified Io version:
add = method(n, block(x, x + n)) a = add(10) b = add(20) a(10) print b(10) print
That is shorter, but how many runtime objects does this create? One less than the original example, obviously. I counted seven [ignoring integers: method( , add( , block( , add( , block( , a( , b( ]. Most users would probably just count the two block( ones and ignore the rest.
-- Jecel
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr <jecel@m...> wrote:
On Thursday 20 November 2003 07:17, Mike Austin wrote:
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr
<jecel@m...>
wrote:
But what is "their enclosing environment"?
B) The activation object, or method-instance for another term
Good.
And who are we talking about in "their proto (parent, actually)
set
to"?
- The block literal object?
- The result of referencing 1 with a "push literal"?
- The block closure resulting from sending 'value' to 2?
- The block literal object
Now we have a problem. Unfortunately, B doesn't yet exist when the parser is creating 1 from the source text. It is likely that you
have
different definitions from what I am used to, so I will try to be clearer below.
Although Io handles locals a bit different, it is essentially the same idea when I do this:
myObject = Object clone do ( ` add = method ( n, ` ` return block( x, return n + x )
The expression "block(...)" is what I called 1 above.
` ) )
a = myObject add( 10 ) b = myObject add( 20 )
a and b are two instances of what I called 2 above.
write( a( 10 ), "\n" ) write( b( 10 ), "\n" )
a( 10 ) and b( 10 ) are two instances of what I called 3 above.
io closure_test.io
20 30
The returned block's proto is set to the new "Locals" instance
that
is created when calling add. It works simply, and beautifully.
Ok, so you set the proto of 2 to B and then 3 gets to inherit it. I haven't paid attention to such details in Io, however, and would be happy for you or Steve to correct me.
It certainly does work, but I wouldn't call it simple. Since Self doesn't allow non-lifo blocks, I'll rewrite your example in
Smalltalk
instead:
MyObject>>add: n ^ [:x | x + n]
a := myObject add: 10. b := myObject add: 20.
Transcript show: (a value: 10). Transcript show: (b value: 10).
I will agree that if you don't care about the multiple steps and
objects
needed to make this work, it looks simple enough. Explaining how to
use
blocks isn't a problem for me, but explaining how they get their
job
done is.
I believe the implementation of closures can be fairly simple in a dynamic language. As for the efficiency - handing stack allocated locals and frames for example, I'm aware that this can get much more complex. Actually, I feel a bit of deja vu here, must of talked about it in the past :)
-- Mike
On Friday 21 November 2003 03:28, Mike Austin wrote:
I believe the implementation of closures can be fairly simple in a dynamic language.
Sure!
As for the efficiency - handing stack allocated locals and frames for example, I'm aware that this can get much more complex.
I am not worried about implementation tricks, but the "story" that gets presented to the programmer. Self doesn't actually clone method objects in the virtual machine, for example. The implementation can be as complex as needed, as long as only implementors have to deal with it.
Actually, I feel a bit of deja vu here, must of talked about it in the past :)
This is probably the fourth time I have ranted about blocks in this list. Sorry for being repetitive, redundant and repetitive.
-- Jecel
Have you arrived at an answer for blocks in Caper?
-- Mike
--- In self-interest@yahoogroups.com, Jecel Assumpcao Jr <jecel@m...> wrote:
On Friday 21 November 2003 03:28, Mike Austin wrote:
I believe the implementation of closures can be fairly simple in a dynamic language.
Sure!
As for the efficiency - handing stack allocated locals and frames for example, I'm aware that this can get much
more
complex.
I am not worried about implementation tricks, but the "story" that
gets
presented to the programmer. Self doesn't actually clone method
objects
in the virtual machine, for example. The implementation can be as complex as needed, as long as only implementors have to deal with
it.
Actually, I feel a bit of deja vu here, must of talked about it in the past :)
This is probably the fourth time I have ranted about blocks in this list. Sorry for being repetitive, redundant and repetitive.
-- Jecel
From: "Mike Austin" mike_ekim@yahoo.com:
Have you arrived at an answer for blocks in Caper?
My intellect shut down a couple of weeks ago, and I'm working to restart it. When I succeed, I shall address your question and other points raised in this debate.
Cheers, Paul
self-interest@lists.selflanguage.org