Extra Ideas
Ka-Ping Yee
ping@lfw.org
Mon, 19 Oct 1998 02:31:33 -0700 (PDT)
Further to the message "Good Things About E":
Objects with a run method are like functions -- we can turn a
"function" into a "method" in Python like this:
>>> class Dog:
... def bark(self):
... print "woof!"
...
>>> d = Dog()
>>> d.bark()
woof!
>>>
>>> def newbark(self):
... print "arf!"
...
>>> newbark
<function newbark at 80ad768> # a plain old function...
>>> Dog.bark = newbark
<unbound method Dog.newbark> # magically becomes an unbound method
>>> d.bark()
arf!
>>>
In E we can do this by delegating the method call:
? define DogMaker() {
> define barkfunc() {
> println("woof!")
> }
> define Dog { # note 1
> to bark {
> barkfunc
> }
> to setBarkFunc(func) {
> barkfunc := func
> }
> }
> }
# value: <DogMaker>
? d := DogMaker()
# value: <Dog> # note 2
? d bark
woof!
? define newbark() {
> println("arf!")
> }
# value: <newbark>
? d setBarkFunc(newbark)
# value: <newbark>
? d bark
arf!
?
(note 1: I would like to have written "define self" here,
as i assume this would mean that the code inside the object
would refer to the object as "self" rather than "Dog".
However, i suspect this would cause us to get, at note 2,
"# value: <self>", which isn't very informative. ..?..)
What about going the other way -- getting a "function"
from a "method"?
In Python this is simply
>>> d.bark
<method Dog.bark of Dog instance at 80acf48>
It can be invoked like this (the bound method object knows
both the function code and the instance):
>>> f = d.bark
>>> f
<method Dog.bark of Dog instance at 80acf48>
>>> f() # operates on d
woof!
Or, you can get the unbound method directly from the class:
>>> Dog.bark
<unbound method Dog.bark>
>>> g = Dog.bark
>>> g
<unbound method Dog.bark>
>>> g()
TypeError: unbound method must be called with class instance 1st argument
>>> g(d)
woof!
To provide the interesting abilities of functional
programming languages, do we need a macro which expands
method(object, <method>, arity)
into
define _ { # note 3
define obj := object # note 4
to run(arg1, arg2, ...) { # note 5
obj <method>(arg1, arg2, ...)
}
}
so as to bring any particular method "to the front" as
the one standing in for the lambda?
(note 4: in some other languages you could write the
equivalent of "define object := object" to bring in
the current value of "object" from an outer scope.
What does "define object := object" mean in E?)
(note 3: this brace means "create me an object";
note 5: this brace means "make a me a new scope":
this needs to be pointed out to the E beginner.
There isn't any other way than "define _" to say
"make me an object" without "define"-ing anything
to be initialized to it, is there?)
Then, do we provide reduce (fold), map, filter, etc.?
Allow me to attempt an exercise in E...
define fold(func, start, list) {
switch(list) {
match [] { start }
match [car] + cdr { func(car, fold(func, start, cdr)) }
}
}
Does that look right?
On a totally different tack: now that we have optional type
information, will we need something like attempted assignment
(?=) to take the place of dynamic casting?
!ping