Hacker News new | ask | show | jobs
by pfdietz 2310 days ago
I've been doing some functional style programming in Common Lisp, and I was wondering what exactly should be considered functional programming.

In particular, is object identity with EQ consistent with functional programming? Constructors do not act like functions if EQ is the equality. Or should that be more "immutable programming"?

Common Lisp, because it has EQ and object identity, cannot perform some optimizations that a truly functional language's implementation could. In particular, it cannot combine equivalent function calls, and cannot merge equivalent data (hash consing or the equivalent.)

2 comments

I really enjoy Common Lisp being imperative enough to make iteration and collection orthogonal. You can freely mix-and-match functions for traversing data structures (dohash, maphash, etc) and ways to accumulate values (setf, push, ext:collect, etc.) I find that equivalent purely functional code often needs more functions to both traverse and collect at the same time e.g. fold, map, flatmap, etc.

I know that the functional universe has solutions to these problems, like the State monad and reusable collections protocols based on common minimal primitives, but I really appreciate being able to just freely string together tokens like DOLIST WHEN EXT:COLLECT without any elaborate frameworks.

So I suppose that programming in Common Lisp makes me appreciate non-functional programming.

Having said that, I do feel like a caveman every time I spend brain cycles on picking between EQ/EQL/EQUAL/EQUALP/STRING= or worrying about whether the object I'm updating might come from a quoted constant and invite undefined behaviour. Hard to have it all...

I would far rather have CL's explicit representation of the different types of equality, than many languages approach of either a) "you can't do that" or b) just getting it wrong sometimes. So I would hardly call that behind the times, although the interface could be nicer I suppose.
Immutability is something I'm still exploring in Common Lisp. Any pointer, anyone?
I recommend Fset, which other commetners have mentioned.

I wrote a wrapper for it and SERIES, called folio, followed by a revised version called folio2. If your requirements resemble mine when I wrote it, you might findit useful.

You can find it here:

  https://github.com/mikelevins/folio2/
Thanks for sharing!
What I do: Write pure functions, but don't shy away from mutating lexically scoped state in order to improve performance. For global stuff, wrap it with classes since objects are a commonly understood way to reason about state.
For immutable data structures, there is this helper library which can help: http://quickdocs.org/immutable-struct/
It's a matter of not modifying things once constructed. CL has hooks to help do that, although a user can always get around them.

A package I've been involved with lately is fset, which is available through quicklisp, or at

https://github.com/slburson/fset

It has some interesting features, including "functional setf expansion". This would turn something like

(setf (fcar x) y)

into something equivalent to

(setf x (cons y (fcdr x)))

(where "fcar" and "fcdr" are the same as "car" and "cdr", except when they are in a setf-able place form.)

(fcar and fcdr are not in fset; I used those names just for exposition here.)

It could work with nested accessors, but only if it bottoms out in a variable.

Are you aware of any memory-leak issues with fset? I once used it in a game where I updated an fset sequence in real-time. I let it run for a while, after coming back I noticed my computer was completely frozen (out of RAM).
It's inspired by Clojure after all. The language of choice for users that don't care about performance.
It probably isn't. The initial public release of Clojure was in the fall of 2007. Scott Burson has been working on FSet since at least 2004. He himself says it's inspired by Refine.
I stand corrected. I still maintain my point about (idiomatic) Clojure being terribly slow though.
Didn't know about this, it looks very cool indeed! Thanks for sharing!
https://github.com/smithzvk/modf like setf, but doesn't mutate the data (didn't try).
https://common-lisp.net/project/fset/

By the way, I found a lot of your best practices questionable to say the least. Since it's obvious that you are a newcomer to CL, I would refrain from producing "best practices" type blog posts until I had a few years of experience under my belt.

For that reason, I also found your post confusing and I'm inclined to categorize it as "mostly rehashing stuff that is already there" rather than strong signal. There is ample, good, introductory material for CL on the net, we should strive to think before we dilute it with derivative posts.

Thanks for the fset link.

I understand your comment as a bit judgmental, if not rude. Please keep the tone friendly, it helps if we want to have constructive discussions. I'd be happy (as well as everyone around here I'm sure) to discuss what you find questionable.

I agree with GP about refraining from "best practices" type advice -- at most leave it as "I prefer doing it this way". (Example: There was no mention of qlot for a form of version pinning, which would be considered a "best" or at least "standard" practice in other ecosystems, but while I use it I'm not going to insist other Lispers I don't work with do so too. They can keep doing things how they like. Common Lisp is not for the strongly opinionated.)

Apart from that though I do think it's useful to have the occasional new posts showcasing rebuttals to a few "myths" that won't die, so I appreciated your post. Some gifs of editor interactivity (like in https://malisper.me/debugging-lisp-part-1-recompilation/) would make it better. Though ironically (one myth has been "it has no libraries") perhaps some people might be turned off by the mention of so many libraries ("ok such-and-such is there after all but I have to type a quickload, why can't it come without any work?" -- programmers are often lazy in the worst ways). Before using every utility library under the sun and then some, I'd encourage people to get to know the base language... It's typically quite sufficient for many tasks. But hey, libraries are cool too.

Two other persistent data structure libs that came up in a thread the other day (though fset seems to be the most popular): https://github.com/danshapero/cl-hamt/ and https://github.com/ndantam/sycamore

Right, maybe "best practices" was poorly worded. I'll think of something better.

Regarding the visual aspect: Very nice link, thanks for sharing! I was also thinking of showing off the macro-stepper.

Regarding the lack of libraries: well, Quicklisp is strong of some 1500 libs, which is rather poor compared to most popular languages out there. So yes, it goes both ways!

I'll check out the other libs, thanks!