Calling a method by name, dictionaries or getting a reference to a method

17 views
Skip to first unread message

Mike Austin

unread,
Oct 30, 2011, 4:12:30 AM10/30/11
to magpi...@googlegroups.com
For something I'm playing around with, I need one of these.. and I seem to have struck out.

1. I couldn't find how to call a method dynamically by name, such as foo["bar"]() in JavaScript
2. I couldn't find a dictionary/map class which would allow me to build #1
3. I couldn't find any way to reference a method by signature, such as
    val foo = bar  // Where bar is the name of a method (of course more complicated with multimethods and type signatures)

As I understand it, records are like tuples, so you can't use them quite like dictionaries.. unless I'm wrong.  Does anyone have any ideas?

Mike

Bob Nystrom

unread,
Nov 3, 2011, 1:44:18 AM11/3/11
to magpi...@googlegroups.com
On Sun, Oct 30, 2011 at 1:12 AM, Mike Austin <mike.aus...@gmail.com> wrote:
> For something I'm playing around with, I need one of these.. and I seem to
> have struck out.
>
> 1. I couldn't find how to call a method dynamically by name, such as
> foo["bar"]() in JavaScript

Magpie doesn't let you do that. If it did, it wouldn't look like that
because methods live in lexical scope and aren't actually looked up on
the object.

Even so, it also doesn't let you refer to methods in lexical scope by
name either. This is more or less by design because I'm trying to
avoid some of the bad performance implications of having being able to
refer to things by name.

> 2. I couldn't find a dictionary/map class which would allow me to build #1

Maps have been on my todo list for a while...

> 3. I couldn't find any way to reference a method by signature, such as
>     val foo = bar  // Where bar is the name of a method (of course more
> complicated with multimethods and type signatures)

Strangely enough, methods aren't first-class in Magpie. They live in a
different namespace from variables and you can't get a reference to
one without invoking it. Functions, however, *are* first class, so you
can first class-ify a method by making a little function that invokes
it:

def (left) foo(right)
print(left + " foo(" + right + ")")
end

var wrapper = fn(left, right) left foo(right)

Now you can pass around wrapper and invoke it as you please. Note that
you have to use the call method to call a function:

wrapper call("left", "right")

>
> As I understand it, records are like tuples, so you can't use them quite
> like dictionaries.. unless I'm wrong.  Does anyone have any ideas?

Records are kinda-sorta like dictionaries but more limited: they are
immutable and can only be created from literals. Records are to
dictionaries as tuples are to lists. I've gotten to lists (and arrays,
their immutable brother), but I haven't implemented maps/dictionaries
yet.

If you were feeling crazy, you could implement one yourself completely
in Magpie, but that's likely the kind of thing that should be provided
directly by the language for performance reasons.

- bob

Mike Austin

unread,
Nov 3, 2011, 2:03:24 AM11/3/11
to magpi...@googlegroups.com
I was going to try implementing a dictionary using records since you can extract single records :)  And regarding method signatures, I did just what you said the other day - creating anonymous functions that call a method.  Here's a snippet (I always seem to write a windowing system when I'm learning a new language):

val events = mouseDown: fn(view, point) view mouseDown(point),
             mouseUp:   fn(view, point) view mouseUp(point)

val mouseDown: type = events
view sendEvent(MouseEvent new(Event: (type: type), point: point))

Here, I'm using an anonymous function as the event "type", so that it calls the appropriate method.

def (this :: View) sendEvent(event :: MouseEvent)
    print(">>>", this subviews)
   
    for subview in this subviews do
        if subview contains(event point) then
            return subview sendEvent(event point - this origin)
        end
    end
   
    (event type) call(this, event point)

    return this
end

There are probably bugs here, because I'm writing from memory :)

Mike

Bob Nystrom

unread,
Nov 3, 2011, 2:19:11 AM11/3/11
to magpi...@googlegroups.com

Very nice. That looks exactly like I'd hoped this would work.

One thing to consider: if you find yourself often needing to select a
method parametrically like this, I think that's a hint to actually
make a parameter. What you're doing here is fine, but you could also
have a single mouseEvent method that took an argument for the type
that you could then specialize on, like:

def (view is View) mouse("down", point is Point)
print("mouse down at " + point)
end

def (view is View) mouse("up", point is Point)
print("mouse up at " + point)
end

view sendEvent(MouseEvent new(Event: (type: "down"), point: point))

def (this is View) sendEvent(event is MouseEvent)
print(">>>", this subviews)

for subview in this subviews do
if subview contains(event point) then
return subview sendEvent(event point - this origin)
end
end

mouse(event type, event point)

this
end

Since methods can specialize on values too, it's pretty easy to make a
set of methods that you "switch" on by some value.

- bob

Mike Austin

unread,
Nov 3, 2011, 3:47:48 AM11/3/11
to magpi...@googlegroups.com
Not to mention it again, but now I remember that's how Dylan's DUIM worked also.  I keep forgetting to let magpie just match patterns - so used to dumb languages :)

Updated:

def (this is View) handleEvent("mouseUp", point is Point)
    print("mouseUp", point x, point y)
end

This is a lot cleaner than double and triple dispatching to get the same functionality.

Mike

Joe Groff

unread,
Nov 3, 2011, 1:27:04 PM11/3/11
to magpi...@googlegroups.com
On Wed, Nov 2, 2011 at 11:19 PM, Bob Nystrom <munifi...@gmail.com> wrote:
One thing to consider: if you find yourself often needing to select a
method parametrically like this, I think that's a hint to actually
make a parameter.

It's useful to be able to lower a method to a parameter. You could have a syntax for symbol objects, which implicitly have methods on a "call" generic matching their own methods, e.g., #foo call(a, b, c) would be equivalent to a foo(b, c). This is a design that seems to work well in other languages like Ruby.

-Joe

Bob Nystrom

unread,
Nov 4, 2011, 9:43:01 PM11/4/11
to magpi...@googlegroups.com

Right. We may go in the direction at some point, but I'm trying to see
how far we can get without actually referring to things by name.
Avoiding doing so I think makes it easier to compile to efficient code
(all identifiers can be erased) and makes things like tree shaking
(removing unused code) easier and I don't want to give those up unless
we get something really useful in return.

- bob

Reply all
Reply to author
Forward
0 new messages