[racket] Where Are List/Container Operations Headed?

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Mon May 16 10:00:20 EDT 2011

Bottom line: you are correct. First, consistency 
matters and Racket is inconsistent in this regard. 
Second, placement of 'major' arguments matter (I 
dislike your use of 'container' here but I figure
I know where you come from). 

re: consistency. Racket, like most languages, is 
a historically grown, organic artifact. As such 
various historical accidents have shaped the language. 
As the designers of artifacts, we should take the time
to fix such inconsistencies on a regular basis, but we
haven't -- partly because of legacy code and partly 
because these inconsistencies don't rank as high on 
our list as other problems we need to fix. 

re: major argument. In contrast to OOPLs, FPLs have
wrestled with this issue for decades. Eli points out
amusing little programming 'tricks' that shaped some
interfaces -- and it is sad because it reveals that we
lack(ed) a design philosophy. 

In my personal opinion, we should design interfaces
like this: 

 f1 : major-arg minor-arg1 ... -> result1 
 f2 : major-arg minor-arg2 ... -> result2
 fn : major-arg minor-argn ... -> resultn

where these things are types or contracts for many 
reasons. A side effect would be that readers would 
notice how close FP is to OOP and that programming 
well in either world takes reasonably similar design 

(Plus, if you decide to switch to our classes, just 
eliminate major-arg and you have method definitions.) 

-- Matthias

On May 15, 2011, at 3:47 PM, Deren Dohoda wrote:

> Please forgive my ignorance on this matter.
> (cons value list)
> --- Well, makes intuitive sense to put the container second here.
> (map proc list)
> --- Reads like English.
> (foldl proc init list)
> --- Hmm, I guess the association with "map" makes this understandable.
> (take list 2)
> (drop list 2)
> --- OK, what? Why not (take 2 list)?
> I am not a programmer by trade, but this kind of plurality of
> interface is slightly disturbing. When I am working on programs, I
> inevitably look to put containers first. I'm not sure it is always
> very pleasing, but it makes *-like procedures a bit friendly. E.g.,
> (define (push stack literal)
>  (cons literal stack))
> (define (push* stack . literals)
>  (foldl (lambda (lit st) (push st lit)) stack literals)) ;stupid lambda
> Additionally, it can help with syntactic sugar if argument placement
> has some consistency to it.
> And there is the germ of it. A small issue, really, but composing
> container procedures becomes extremely ad hoc because the container
> constructors and disassemblers themselves seem a bit ad hoc. The
> example itself is a little trivial, I know, but what it means is that
> if I later decide to change my stack representation with some other
> container---hand-rolled or otherwise---the plumbing is not
> particularly extensible unless, from the outset, I roll my own
> container procedures and use them instead. (For larger programs, I
> have.)
> In short, abstracting containers is not friendly, because built-ins
> seem to be a mix of ideas.
> This is not a complaint, though, just a question. (If I complained
> about every oddity I'd never have programmed in any language!) Where
> might Racket be going in the future with this? Nowhere? Ideas still
> percolating? Has anyone roughly solved the issue? I know clojure
> attempted to (perhaps successfully) wrestle with this issue, but I
> never looked very far into it. Different containers are often
> different for a reason, and maybe this is not a problem that has a
> "solution."
> Thanks for any insight, everyone,
> Deren
> _________________________________________________
>  For list-related administrative tasks:
>  http://lists.racket-lang.org/listinfo/users

Posted on the users mailing list.