4 ms·
I think you'd have to be a bit more specific about what you mean by "manage" and the specific use-case/management of side-effects that would be required. I'm n
by ACow_Adonis 5y ago
I think you'd have to be a bit more specific about what you mean by "manage" and the specific use-case/management of side-effects that would be required. I'm not personally convinced that it's been shown that purely functional languages provide comparative utility when comparing programs of similar length and complexity for non-functional ones, and I'm personally pretty sympathetic to pushing people towards "functional style" (as defined by me) because it's just a good way to program.
I think giant programs that have to interact with the real world are nightmares regardless, myself. And small programs written in a value-passing functional style by competent programmers don't really have very many issues with managing state, in my experience.
The short answer is Common Lisp isn't really "functional": it doesn't manage side-effects. You can just go imperative/side-effect crazy if you want. Just "setf all the things" or use CLOS for objects everywhere (i almost never actually use CLOS or complex objects, so maybe that's why I'm a bit unsure of what there is to manage).
The long answer is, i guess, a combination of several features and conventions:
- A number of internal functions offer two versions: a destructive and a non-destructive version. (remove) and (delete) are examples of two functions that do the same thing in the abstract, but one is allowed to mutate it's target list, primarily for efficiency reasons.
- Regardless of the above, you tend to program in a universal style of accepting and doing things with return values from functions: so you explicitly "manage state" in the sense that you might assign return-values to variables, but it would be extremely weird to actually rely on a destructive side-effect of a function itself without treating it's result like a returned value of a function. To use a function for it's destructive side-effects isn't really done, and even if it IS, you'd treat it in a style that would be about treating it AS THOUGH it were just a returned value from a function. Almost universally you only use the destructive version for some computational efficiency reason, never because you expect to predict the outcome of what would happen to the underlying memory that you're mutating. That's left up to the compiler to give you the correct answer (and it may or may not mutate the underlying thing at all). You just accept that the function gives you the correct result.
- The various name-spaces tend to separate out clashes and management on that front: values go in one name-space, functions/macros go in another, and packages and things are another demarcation (i recall there's technically more name-spaces, but those are the main ones).
- There's no official "global variables" in the "normal" sense of the word. There are dynamic and static variables, which might be analogously thought of as global variables/values, but those tend to only be used for their specific use cases, and convention is to mark them as special exceptions. Variables with values in them are explicitly declared in functions, and you're responsible for choosing to manage state within functions, but due to the abstraction of functional boundaries, you'd generally never think of what mutations are actually going on inside a function unless you're authoring it.
- garbage collection and things dropping out of scope generally just take care of most things in practice.
- If you REALLY got into some sort of nitty-gritty bit-twiddling state nigthmare, you'd fall back on macros/testing/formal proof to help you. But most people are not doing that.
- Multi-threading isn't part of the spec IIRC, so that depends on the compiler extension and the problem. Race-conditions and ambiguities in that respect aren't really handled by the common lisp spec.
- These days with the advent of test driven development, there are testing frameworks that can be used for complex programs, but I'm hesitant to say anything in that regard as CL was really written before that became a cultural thing.
Of course, with all of these, there's the assumption that you can generally write in some functional/non-destructive style if you so choose. So if that solves the problem or makes your problem simpler, that's generally what you'd do. And if it doesn't, then it's not apparent that functional-ness of your problem is really the main problem at hand...
I hope that helps, feel free to ask me a few more questons if it helps, but also take note that it's been a few years since I've done anything with Common Lisp :\
Edit: I should say, if you're working with things like "inifite structures" or "delayed evaluation", you'd generally have to manage that explicitly yourself somehow, as CL is generally eager in it's evaluation style, but that's not most problems in my experience.