Arc Forumnew | comments | leaders | submitlogin
2 points by almkglor 6091 days ago | link | parent

Hmm. Mockup of how it would be used.

  ; In arc.arc, or maybe something equivalent in
  ; ac.scm.
  (typeclass 'cons 'car 'cdr 'scar 'scdr 'apply)

  ; In our library, where we can't (or at least,
  ; "shouldn't") redefine the built-in Arc 'cons
  ; typeclass.
  ; A perfect subset of 'cons.  
  (typeclass 'scanner 'car 'cdr)

  ; Dumps out a character scanner or list
  ; to a port
  (def dump-out (port l)
    ; Note: we expect has-a to return t
    ; even if l is a 'cons - i.e. the type
    ; system should be smart enough to
    ; realize that 'cons provides all the
    ; interfaces of 'scanner.  Prolly something
    ; like:
    ; (= all-functions (mappend [instance-functions-of _] (type-classes-of ob)))
    ; (all
    ;   [some _ all-functions]
    ;   (instance-functions-of requested-type-class))
    ; above can be optimized by using tables - put all
    ; of the requested type class's instance functions
    ; in the table, then remove everything that exists in
    ; all-functions; if we finish all-functions while
    ; table entries exist, we know the object doesn't
    ; have that type class
    (unless (has-a l 'scanner)
      (err "Not a scanner"))
    (map [write _ port] l))


5 points by nex3 6091 days ago | link

Now, the thing I like about implicit has-a relationships is that they guarantee less code than making it explicit, in that they're the same amount of code sans type declarations and checks. For example, the above would be simply

  (def dump-out (port l)
    (map [wrie _ port] l))
No typeclass declarations and no type checking. This also has the advantage of being identical to the code you'd write if you were completely unaware of a scanner abstraction.

Also, on a more philosophical level, I feel like type checking is just out of place in a dynamically-typed language like Arc.

-----

4 points by almkglor 6090 days ago | link

Not really my point. The point is that the trivial check:

  (unless (has-a a 'scanner)
    (err "type error"))
May be closed into a macro:

  (mac type-declaration (a typ)
    `(unless (has-a ,a ,typ)
       (err "type error")))
...which ends up being the optimizing compiler's clue:

  (def dump-out (port l)
    ; a completely optional declaration
    ; for:
    ; (1) an non-optimizing interpreter, so that
    ; you have some chance of checking errors, and
    ; (2) the optimizing compiler, so that it can
    ; do some shortcuts.
    (type-declaration l 'scanner)
    (map [write _ port] l))
^^

You can then trivially redefine type-declaration as:

  (= type-declaration
     (annotate 'mac nilfn))
To disable type-checking.

Oh and yeah: type checking is plenty in-place, if you're building a library. If the library's interface functions simply passed the object to library-internal code without checking, the error will pop up as coming from some code deep in your library, where your user might be less inclined to trace (because it might be your bug, not theirs). At least if you do the check on the interface functions themselves you have proof that the bug is in the user's code.

I agree it's out-of-place in most apps of course.

-----

4 points by nex3 6090 days ago | link

But that macro, and the whole plumbing behind declaring and checking type classes, just don't have to exist if you make it implicit. It's a lot of added complexity for no gain in power. All you gain is a little type safety - and if you need type safety, Arc (and Lisp in general) is not your language.

I wonder how much type-checking major Lisp libraries really do have. In the dynamically-typed-language libraries I've looked at, there are more or less no type checks. In the Ruby community, they're specifically discouraged, because they limit the power of duck typing.

-----

3 points by almkglor 6090 days ago | link

Yes, but who says it has to be used? In the majority of code, there won't be type checking, but we do want to say something like this:

  This function accepts a list of ordinal types
which we can simply put in-code as:

  (type-declaration a 'Scanner)
  (type-declaration (car a) 'Ordinal)
The point is that the function doesn't specifically accept a list of strings or numbers - it accepts a traversable sequence of anything that can be ordered. The type-declaration thing is just a notation to express that to someone else.

-----