Arc Forumnew | comments | leaders | submitlogin
4 points by nex3 6144 days ago | link | parent

That sounds about right. My thought process was more along the lines of CLOS-style generic functions, but I think these are roughly equivalent to a more generalized notion of C++'s instance methods.

As vehemently as I may argue, I'm not actually entirely convinced that this way is best. I come from a Ruby tradition, which is deeply based on the message-passing object model, which ends up behaving a lot like type classes. I've seen this end up working very nicely in practice, facilitating duck typing and allowing all sorts of cool tricks via encapsulation.

At the same time, though, CLOS is supposed to be very excellent. And the one thing, more than any other, that appeals to me about a generic-function style object system is that it can be implemented in pure Arc. It doesn't require any more axioms than the very-simple type, rep, and annotate (née tag).

The message-passing/type-class model, on the other hand, requires what seems to me to be an incredibly radical change to the core of the language: built-in per-object tables. This just strikes me as fundamentally un-Lispy. In a sense, it eclipses lists as the fundamental data type - they're not much more than tables with "car" and "cdr" keys.

While this may be interesting territory to explore, it's being explored in other languages - Lua and Javascript both explicitly regard hash tables in the same that Lisp regards lists.

Also, from a more practical sense, I'm not convinced that the message-passing/type-class model offers anything that generic functions don't. The main benefit that I've seen is duck typing - the ability of a function to rely on its input implementing to a given interface (in this case, having functions work properly on it), rather than being a given type.

But I think this works just as well whether or not the core functions are implemented with a type-class-style system or a generic-function-style system.

Consider, for example, Ruby's favorite duck type: enumerable objects. In Ruby, every object that implements an each method that calls a lambda on each element can be declared "Enumerable," and get various methods like map for free. This can be done using attachments like so:

  (let (foo bar baz) ("foo" "bar" "baz")
    (add-attachments
      'each (fn (f)
              (f foo)
              (f bar)
              (f baz))
      nil))

  (def each (obj f)
    ((get-attachment 'each c) f))
or using generic functions like so:

  (= foo (annotate 'mytype '((foo "foo")
                      (bar "bar")
                      (baz "baz"))))

  (redef each (obj f)
    (if (~isa obj 'mytype) (old obj f)
        (do
          (= obj (rep obj))
          (f (car obj))
          (f (cadr obj))
          (f (cadr:cdr obj)))))
However, in either case, map can be defined as simply

  (def map (obj f)
    (let l nil
      (each obj [push (f _) l])))
The point of all this being that duck typing (or whatever you want to call the versatility granted by assuming a type implements an interface rather than specifically checking its type) is independent of whether or not the interface is implemented by attaching functions to objects or by defining generic methods.