Method Dispatch Syntax
or, on the equivalence of the dotted member/method notation and the functional dispatch form.
Methods and functions
In Haskell
- method num1 num2 // call this the functional dispatch form
is somewhat akin to
- num1.method(num2)
in C++/Java or Python-like languages. For example, strings have a method called join
- ''.join(list) ---> join('', list)
In Python, len(list) reminds us of list.length() in JavaScript, but why are there two distinct (non-equivalent) ways of saying this? Why is list.len() not part of Python? Because len is a built in operator for sequence types (dict, tuple, list, string) or classes that fake it (with a method __len__().
This is an example of building things into the language that could very definitely be generalized or built out of simpler constructs (like the way Scheme or Haskell do things, always in a simpler, more general (less ad hoc) way).
Fields
A separate notion got put into the mix with C++/Java (and Python kept it) in the OOP world
- scalar = obj.field // getting a value out of a struct
or
- obj.field = scalar // injecting into a struct
which reminds us of
- scalar = obj["key"] // lookup in a dictionary
or
- obj["key"] = scalar // insert into a dictionary
which is very similar to
- scalar = list[4] // sequence lookup (list, string)
or
- list[5] = scalar // injection into a sequence (list, string)
The connection comes direct from a language called Lua, where
- obj.field = scalar
and
- scalar = obj.field
are both syntactic sugar for
- obj["field"] = scalar
and
- scalar = obj["field"]
This is nice because it feels uniform. In this case obj is a table (similar to a dictionary in Python), which can also have numerical indices for lookup.
Observation
There is a strange problem of ambiguity if you want be able to store a function in a record struct. For example, how do you look up and apply method meth from object obj?
By itself,
- obj.meth ===> obj["meth"]
not
- obj.meth ===> meth obj
But
- obj.meth(param) ===> obj["meth"](param)
and
- obj.meth(param) ===> meth(obj, param)
Seem equally valid.
We have to allow records to contain (pointers to) functions or functions would not be totally first class.
The goal of allowing this weird notation of obj.meth is to imitate how useful (and understandable) Python string manipulation is, when read (and written!) from left to right. If you type method calls in the order that you do them, then in the Python way it goes from left to right; in Haskell you have to double back and write the method calls to from right to left, adding parentheses to both sides of the expression. Wait... what about the . (composition) operator? Or the $ function application operator that can be used to avoid having to type the right parentheses?
I guess you can just define an operator (like --> or something) that works like $ in reverse.
Example:
- obj-->meth(param)-->meth2(param2) ⇒ meth2 (meth obj param) param2
which is a lot easier to write (in that order), and, as before,
- obj.meth(param) ⇒ obj["meth"] param
Note that obj["meth"] param the dictionary lookup is actually done at runtime.
Also, the ability to use a function as infix is really nice, e.g. in Python
- ' - '.join(['a', 'b', 'c']) ⇒ 'a - b - c'
becomes
- " - " `join` ["a", "b", "c"] ⇒ "a - b - c"
but join doesn’t have to be a builtin method for strings; instead it can be grafted on after the fact in Haskell (which it is).
Conclusion
Haskell rocks. Also, I should read and write about slots in Smalltalk and Self (or Slate) because these people thought about all of this. To be continued....