Can Lisp be easily used in an immutable, functional manner?
Asked Answered
H

1

7

I come from a background in Haskell, and I want to learn Common Lisp.

It's well discussed that CL is "not a functional language", but I would like to know whether it can be used as a functional language.

For example, is it possible to use all of the data types in an immutable manner? Common Lisp hash-tables seem to be set using setf, which is decidedly of the mutable orientation. Is there a way to use it in an immutable manner?

Aside from the "IO" aspect of Common Lisp (the "purely" aspect of Haskell) where I interact with files, networks, etc., can I comfortably write code in Lisp and be assured that the code will have referential transparency?

Can I expect these kinds of properties from the popular libraries available in Common Lisp?

Are there common idioms or aspects of the language that will make these difficult?

Headrail answered 26/1, 2019 at 3:38 Comment(8)
Common Lisp was deliberately designed to accomodate at least imperative programming, functional programming, object-oriended programming. The way in which you use it depends from you, of course. If you write programs without any kind of side-effect you will produce a program with referential transparency (in other words, in the language specification the possible side effects of the operators are well documented).Areopagus
Learning Common Lisp like it is kind of a Haskell with parentheses isn't really that useful, because Common Lisp is a vastly different language and eco-system. 'when in Rome, do as the Romans do'.Dorladorlisa
You say It's well discussed that CL is "not a functional language", but my interpretation is that it is not only a functional language. If Lisp isn't a functional language, what is?Straitlaced
See e.g. github.com/danlentz/cl-ctrieStraitlaced
@RainerJoswig isn't it part of the Lisp philosophy though that the language should adapt to the use case, rather than enforcing everybody to use it in the same manner?Singlefoot
@Singlefoot : why ignore the existing Common Lisp language with 1000+ operators, data types, control structures, variables, ... probably better than being disappointed that CL is not Haskell and only a bad Haskell substitute. Also keep in mind that shaping a language to support side-effect free programming in an efficient way might not be a beginner task without some deeper knowledge of the language...Dorladorlisa
"a way to use [anything] in an immutable manner" is to always mutate a copy, never an original. -- CL is an imperative language, IMO. Any imperative language can be used to code functionally (e.g. C). The separate evaluation rules and namespace for functions will make it feel very alien, I think, compared to Haskell. But anything is possible in CL.Casebook
I would say CL's strength is that it is multiparadigm. Sure, you could mold the language to your will and wish. But also as well a part of CL's philosophy is to use the different paradigms (FP, OOP, imperative, logical programming, ...) for the proper parts of the program. And in terms of performance it will be very efficient to use mutation - but in a restricted way.Preservative
Z
16

Yes, of course, but depending on how much you want restrictions to be enforced, your mileage may vary.

Just don't mutate

You can simply avoid setf and all the places machinery. If you have to use hash-tables or vectors, you will be copying a lot, but for many applications, the garbage collection overhead can be still manageable. In many places, you can use alists or plists, which can be handled almost like functional data structures. Some useful utilities are in alexandria, e. g. copy-hash-table, which has a key argument to actually do something like a hypothetical map-hash-table. All the other functional goodness, like higher order functions, map, reduce, remove etc. are available in many flavours. Also, certain use cases can be solved quite declaratively in loop.

There are also libraries that make this style explicitly easier, e. g. modf.

This can take you quite far. I perceive it as easy to see where you break referential transparency and avoid that.

Use functional data structures

There is FSet, which gives you a lot of functional data structures, and there is sycamore, which gives some more.

Use transducers

SERIES appeared in CLtL2 in 1990 and almost made it into the standard in 1994. There is also taps, which adds some useful functionality.

Meditate

Personally, I am not so hard after purity any more. Sometimes, mutating a place in a loop is easier to understand than juggling multiple bindings in different extents. I just keep the mutation confined; this is similar to a data ownership concept. However, I like to have purity on a larger scale, both in memory and on disk, e. g. append-only logs, databases (bknr.datastore).

Zeller answered 26/1, 2019 at 13:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.