Hacker News .hnnew | past | comments | ask | show | jobs | submitlogin
Lisp with maps instead of lists (thimbleby.net)
65 points by jefffoster on May 16, 2011 | hide | past | favorite | 18 comments


I've been working on something similar for the last few years. It's a neat idea that has some interesting properties: a code block is a triple: code, value and map (and quite possibly a closure so that it can be repeatedly re-evaluated). You can easily build complex iterative lazy structures as well as all the ordinary things like for-loops, function calls etc using the same basic structures.

It's a similar idea, in many ways, to the way Javascript does objects: run some code get a map back. Where it gets interesting is that you can choose to use the evaluated value and the scope that was generated in evaluating it. Objects, maps, code, scope, closures and lazy evaluation become the same thing: you declare a block of code, you evaluate it into a closure (with scope and a stack) you query it for results, rinse-repeat.

A for-loop becomes a function that takes two blocks: one that lazily generates values in and one that lazily consumes them. It is the two closures that remember their state - not the for-loop function. The 'for-function' just passes the current value to the second block to use it.

Javascript, REBOL and Lua do have these capabilities but none have the elegance (nor simplicity) of Lisp. I'd love to see a language that does.


It may be a toy language, but it's a serious idea, one I've often wondered about. I work a lot with both Common Lisp and Javascript and the one killer JS feature that I miss in CL is its maps, or rather its use of the map as its organizing principle. This particularly shines for exploratory programming, since it frees you from having to work very hard when adding, removing, or reordering information in your program: it's trivial to stuff a property into a JS map and trivial to get it back. Since nearly everything is a map, nearly everything has this flexibility. CL's plists seem like they ought to serve this purpose, but they don't (compared to JS) because plists are conses and their identity changes when you prepend new properties to them; also, all null plists are the same, which turns out to be surprisingly restrictive (again compared to JS).

On my project we ended up adding a JS-style property operator to CL and using JS-style maps wherever we wanted this flexibility, which was nearly everywhere. It just compiles to CL hashtables under the hood. This is so valuable, it's natural to wonder what a Lisp with the map as its organizing principle would look like.


In Clojure, the default collection for data modeling is the map (and has literal support). Unlike javascript, properties are namespaced so you can extend someone else map without fearing a nameclash.

Records (which are objects with a user defined types) are maps too so the upgrade is easy (and yes you can add you own fields to an object).


Sure, but maps are still bolted on. That's convenient, but it's not interesting. The interesting question is, what would a language that is to maps as Lisp is to s-expressions look like? The OP is the first person I know of who's tried to answer that.

It's an important question because maps are flexible and flexibility is important for exploratory programming, which is nearly all programming. If there exists a language that is as regular and programmable as Lisp but organized around maps rather than lists, that would be a big deal.


Lua is similar in this regard, but offers more flexible semantics than JS. Further, i believe most luas support TCO.

http://stackoverflow.com/questions/323346/what-can-lisp-do-t...


It's difficult to gauge how serious this proposal slash thought experiment is. The nice thing about a pair is the way it naturally lends itself to iterative processing: There's the CAR and the CDR, the first and the rest. (Are the keys in these maps ordered?)

Not all data is complex enough to merit a map: modeling e.g. a list using lists is perverse in that you're using degenerate single-item maps to model what is a simpler data structure.

And what does a lisp based on maps look like, if this language preserves the homoiconic nature of lisp?

The argument by analogy that a lisp "should" have a "better" data structure than a cons cell because lisp lambdas support multiple arguments, unlike pure lambda calculus, doesn't hold water. Lisps--and even Scheme, which is often incorrectly taken to have as a design goal to be minimalistic to the point of consisting of axiomatic primitives--do not deal in pedantic adherence to theory. Limiting lambdas to a single argument is pointless whereas a cell happens to be minimal yet extremely useful in practice.

Other have written lisps atop vectors as the primitive data type. That's an idea that makes more sense: it's a generalization of the pair to arbitrary length.


" The nice thing about a pair is the way it naturally lends itself to iterative processing: There's the CAR and the CDR, the first and the rest."

While elegant, the convention of cons cells as lists is increasingly going to be a flaw, because using linear recursion to process a list often obscures parallelism vs using a binary divide/conquer description of algorithms. See recent talks by Guy Steel on lisps organized as concatenation trees.

Also, I think Lua is a good existential example that organizing a language on top of a single MAP data structure can be quite sensible.


>While elegant, the convention of cons cells as lists is increasingly going to be a flaw, because using linear recursion to process a list often obscures parallelism

I disagree that it's a flaw. I think the problem here is this idea that we can only have one data type. For me, the main thing C++ got right was the STL. It didn't have one collection type, it had several. Each with its own set of trade offs. That's what we need in Lisp. You want instant insert but don't care as much about traversal? List. Fast random index-based access? Vector. And so on.


I don't disagree that the pair is far free the be-all end-all of primitive data structures. On the contrary, when I'm programming in Clojure, most of my data structures are made out of hashes and vectors.

And most definitely, the need to walk a linked list inhibits parallel thinking (and, just as importantly parallel execution) and so we shouldn't avoid baking richer data structures into lisps. Again, Clojure does this and even Scheme does too, with R6RS with hashes as well as the more established vectors, because, again, Scheme is not about axiomatic minimalism. But to make a hash the fundamental data structure out of which everything else is constructed would be crazy.

There's plenty of room on the world for atoms, pairs, vectors, hashes, and maybe even primitive sets. What I don't know is if this proliferation of primitive types would cause other problems because we'd wind up dedicating so many bits in a pointer to tags to signal a type that we'd restrict the range of atoms too much. But hey, we're living in a 64 bit world now, right?


"But to make a hash the fundamental data structure out of which everything else is constructed would be crazy."

A hash can easily model a vector, including sparse vectors. There are efficient functional implementations such as Clojure's which is based on Bagwell's Array Mapped Tries.

Functional vectors/arrays are an open research problem.

So I would say maps might be very reasonable as the single structure in a functional language.

Also, there are many ways to cope with tagging more types than are practically fit within the low bits of a pointer. The simplest dates all the way back to MACLISP (no relation to macintosh). The idea is to tag regions of memory that hold uniform values. This fits very well with modern memory allocators based on the slab allocator.


Thanks for the tip on Guy Steele's talks!

And I agree that the cons based model of a list hides important properties (parallelism being perhaps the most important one).


From the article: "Thus the result is not a language for real-world use but one that I hope embodies many different unique and novel design decisions in a way that triggers thoughts and ideas in those that take the time to play with it."

So its meant to be more thought-experimental than realistic (not saying your points are incorrect though).


I agree that sometimes pursuing prima facie "ridiculous" ideas to their logic conclusions can sometimes provide insights into problems that have far more real-world utility than you might ever expect when you start down the path.


This is interesting. Maps are themselves functions, taking the key as a parameter and returning its associated value. So when your basic data type is a map, the language is arguably more "functional" than the usual functions + lists combination.


In Fexl (http://fexl.com/code), the basic concept is the function, and data is built from functions.


I've also built something similar that is based on .NET: http://code.google.com/p/metalang/

Maybe we should all team up or something...


This looks like REBOL. :)


We discussed this same article extensively yesterday at https://hackernews.hn/item?id=2552180 .




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: