[racket] Best practices for classes
At Sat, 4 Oct 2014 10:13:14 -0500,
Robby Findler wrote:
> That said, I do think that our integration of generic operations and
> our standard library deserves some more care and perhaps redesign to
> make things hang together better. As it stands, we have some
> conveniences, but they are limited by backwards compatibility concerns
> (like the one in the previous paragraph). I have heard that some are
> considering this as part of "racket2" but I don't know how far along
> those plans are.
Last year, I started work on a generics-based collections library:
https://github.com/stamourv/generic-collections
I agree that something like this should be part of Racket 2, and I think
that this library may be a good starting point.
The closest I have to documentation is the description of the interfaces
involved:
https://github.com/stamourv/generic-collections/blob/master/main.rkt#L748
I started writing a design document, to better explain the big picture,
but I haven't finished it. If there is interest in exploring that space,
I can spend some time to polish it.
If anyone has comments, suggestions, or would like to help, don't
hesitate! I don't currently have time to work on this (but hope to in
the future!), but would be happy to help anyone who decides to.
Here's a brief summary of some of the highlights:
- The library is designed to accomodate generic consumption,
construction and transformation of collections, while preserving the
"type" of a collection (e.g. mapping from lists to lists and vectors
to vectors, not from lists to an opaque sequence type, as the current
sequence functions do).
- The library accomodates both "structural" collections (such as lists),
that are built and traversed structurally, and "indexed" collections
(such as vectors) that are built and traversed statefully.
- Thanks to generics' fallback methods, it's possible for collection
implementers to define a few basic methods, and get a lot of methods
(think all of racket/list) for free, while still being able to
override specific operations.
A collection that implements either the "structural traversal" set of
methods (`empty?`, `first` and `rest`) or the "stateful traversal"
set of methods (`make-iterator`, which returns something that
implements the `gen:iterator` interface) gets `foldr`, `length`,
etc. for free.
A collection that implemements either the "structural building" set of
methods (`cons` and `make-empty` (generics need an instance to
dispatch on, so we can't just have a generic `empty`)) or the
"stateful building" set of methods (`make-builder`, which returns
something that implements the `gen:builder` interface) gets `range`,
`make` (think `make-list`) and `build` (think `build-list`) for free.
A collection that implements traversal and building methods gets all
"transducer"[1] methods (`map`, `filter`, etc.) for free.
- Thanks to generics' default methods, it's possible to retrofit these
interfaces on top of built-in datatypes. This should make that library
a 95% compatible[2] replacement for the current collection operations.
- Operations are n-ary where it makes sense (e.g. n-ary map) and can
operate on different collections (e.g. `(map f <list> <vector>)`).
Vincent
[1] Unrelated to Clojure's transducers. I picked the name before they
were announced. I'm open to naming suggestions.
[2] The library is currently backwards compatible for all operations
except `member`, whose current design doesn't make sense for indexed
datatypes, and is an IMO questionable pun anyway. The library
instead uses `member?`, which takes a failure thunk, and is thus
closer to other, more modern Rackety APIs.