Is there a portable (across implementations) way to create weak references in CL?
If not, what alternative should I use to avoid memory leaks caused by unneeded listeners kept alive solely by being subscribed to events?
In my particular case, said listeners are used to implement functional reactive nodes (i.e. no side effects, only useful for their stored values) so manual unsubscription would be quite inelegant.
Weak references were not standardised by ANSI, and while most implementations have a way to create weak references, the abstractions provided are different. For example, CMUCL, CLISP and SBCL all provide weak references ("weak pointers"), while Franz has weak vectors and Clozure has value-weak hashtables.
There are libraries that claim to provide a portable interface to weak pointers on a range of implementations. I don't know how good they are.
Related
Somewhere I saw some personal notes from someone who was on the ANSI committee. I thought it was Kent Pitman, but a search of his site doesn't turn up anything. Neither does Google.
I'm interested in the background of the decision not to integrate the condition system with CLOS. CLtL2 speaks of it as a fait accompli, and I'm curious as to why it didn't happen.
The condition system was not integrated with CLOS because there were implementations with existing condition systems which were not CLOS-based (they were, in at least one case, flavors-based), because CLOS did not exist at all until pretty late in the standardisation process. Since a condition system has really deep roots in any implementation, requiring those implementations to rip out a great part of their guts in and replace them with some CLOS-based guts would have placed them – the very implementations which had gone out of their way to make sophisticated condition handling possible in the first place – at a huge disadvantage. Doing that would have been both stupid and would have derailed the standardisation process, since the representatives of those implementations would have been considerably antagonised by a decision like that. So the right decision was made.
It was also unclear at the time that CLOS could be made really performant on stock hardware (perhaps this is still unclear, but stock hardware is now so fast and we all happily live with implementations of other languages which are hugely slower than a good CLOS implementation can be so the problem no longer matters). CL was also considered really big (hard to remember when my fully-fledged hairy CL IDE containing the entire hyperspect and all its own documentation is 2/3 the size of my web browser), so people thought about subset implementations which might not contain CLOS but really needed to contain the condition system.
In particular it is worth looking at the CLHS issue (not part of the spec) CLOS-CONDITIONS-AGAIN, from which comes the following text:
The condition system should not be too tightly integrated into CLOS, for two reasons: Some implementations already have a native condition system that is not based on CLOS, and it should be possible to integrate the native conditions and the ANSI CL conditions. Some people would like to define an ANSI Common Lisp subset that does not contain CLOS but does contain conditions.
The problem areas are the use of DEFCLASS, MAKE-INSTANCE, and DEFMETHOD to define and create conditions, rather than using more abstract macros that conceal the implementation of conditions in terms of CLOS, and exposure of the implementation of condition slots as CLOS slots. If user code was written in a more abstract way, it could run in a subset language that did not contain CLOS.
This is not normative text but you can see what people were thinking.
Or even heavily functional styles in non functional/non memory managed languages.
What sort of techniques are there to deal with problems like intermediate garbage? Cleaning up after lazynizess/thunk allocated memory. Performance(since you can't easily share resources between immutable variables if you have to track its progress to deallocate it(smart pointers?)
You might be interested in programming languages with linear or uniqueness types, these can manage resources (and memory in particular). Recent examples: ATS and LinearML.
There have been attempts at "region-based memory management" (e.g. Cyclone), but they haven't lifted off just yet -- regions also allow for (earlier) memory reclamation, but they aren't enough (e.g., there are programs which, when run with region-based memory management, will exhibit unacceptable performance). The two schemes could be mixed, I think.
Back to your question, some ATS programs can run without garbage collection. (I won't say that such programs are written in "functional" style, such as in SML, but in a mix of imperative and first-order functional style.)
The only relevant thing I can think of is how Mlton is eliminating a significant part of garbage collection with a region analysis. It should be possible, in theory, to implement a compiler which will treat an unmanageable and un-annotated pointer leak as an error, and then one would be able to use many functional programming techniques in an entirely manual memory management setting.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I hear all the time that Erlang is a functional language, yet it is easy to call databases or non side-effect free code from a function, and commands are easily ordered by using "," commas between them just like Ruby or another language, so where is the "functional" part of Erlang?
The central idea is that each process is a functional program over an input stream of messages. The result from the functional program is an output stream of messages to others. From this perspective, Erlang is a rather clean functional language; there are no destructive updates to data structures (like setcar in Lisp and most Schemes).
With few exceptions, all built-in functions such as operations on ETS tables also follow this model: apart from efficiency issues, those BIFs could actually have been implemented with pure Erlang processes and message passing.
So yes, the Erlang language is functional, but a collection of interacting Erlang processes is a different thing. Each process is an ongoing computation, and as such it has a current state, which can change in relation to the other processes. Even a database is just another process in this respect.
In my mind, this is one of the most important things about Erlang: outside the process, there could be a storm raging, but inside, things are calm, letting you focus on what that process should do - and only that.
There's a meme that functional languages must have immutable values and be side-effect free, but I say that any language with first-class functions is a functional programming language.
It is useful and powerful to have strong controls over value mutability and side effects, but I believe these are peripheral to functional programming. Nice to have, but not essential. Even in languages that do have these properties, there is always a way to escape the purity of the paradigm.1
There is a grayscale continuum between truly pure FP languages that you can't actually use for anything practical and languages that are really quite impure but still have some of the FP nature to them:
Book FP: Introductory books on FP languages frequently show only a subset of the language, with all examples done within the language's REPL, so that you never get to see the purely functional paradigm get broken. A good example of this is Scheme as presented in The Little Schemer.
You can come away from reading such a book with the false impression that FP languages can't actually do anything useful.
Haskell: The creators of Haskell went to uncommon lengths to wall off impurity via the famous I/O monad. Everything on one side of the wall is purely functional, so that the compiler can reason confidently about the code.
But the important thing is, despite the existence of this wall, you have to use the I/O monad to get anything useful done in Haskell.2 In that sense, Haskell isn't as "pure" as some would like you to believe. The I/O monad allows you to build any sort of "impure" software you like in Haskell: database clients, highly stateful GUIs, etc.
Erlang: Has immutable values and first-class functions, but lacks a strong wall between the core language and the impure bits.
Erlang ships with Mnesia, a disk-backed in-memory DBMS, which is about as impure as software gets. It's scarcely different in practice from a global variable store. Erlang also has great support for communicating with external programs via ports, sockets, etc.
Erlang doesn't impose any kind of purity policy on you, the programmer, at the language level. It just gives you the tools and lets you decide how to use them.
OCaml and F#: These closely-related multiparadigm languages include both purely functional elements as well as imperative and object-oriented characteristics.
The imperative programming bits allow you to do things like write a traditional for loop with a mutable counter variable, whereas a pure FP program would probably try to recurse over a list instead to accomplish the same result.
The OO parts are pretty much useless without the mutable keyword, which turns a value definition into a variable, so that the compiler won't complain if you change the variable's value. Mutable variables in OCaml and F# have some limitations, but you can escape even those limitations with the ref keyword.
If you're using F# with .NET, you're going to be mutating values all the time, because most of .NET is mutable, in one way or another. Any .NET object with a settable property is mutable, for example, and all the GUI stuff inherently has side-effects. The only immutable part of .NET that immediately comes to mind is System.String.
Despite all this, no one would argue that OCaml and F# are not functional programming languages.
JavaScript, R, Lua, Perl...: There are many languages even less pure than OCaml which can still be considered functional in some sense. Such languages have first-class functions, but values are mutable by default.
Foototes:
Any truly pure FP language is a toy language or someone's research project.
That is, unless your idea of "useful" is to keep everything in the ghci REPL. You can use Haskell like a glorified calculator, if you like, so it's pure FP.
Yes, it's a functional language. It's not a pure functional language like Haskell, but then again, neither is LISP (and nobody really argues that LISP isn't functional).
The message-passing/process handling of Erlang is an implementation of the Actor model. You could argue that Erlang is an Actor language, with a functional language used for the individual Actors.
The functional part is that you tend to pass around functions. Most langauges can be used both as a functional language, and as an imperative language, even C (it's quite possible to make a program consisting of only function pointers and constants).
I guess the distinguishing factor is usually the lack of mutable variables in functional languages.
I read somewhere where rich hickey said:
"I think continuations might be neat
in theory, but not in practice"
I am not familiar with clojure.
1. Does clojure have continuations?
2. If no, don't you need continuations? I have seen a lot of good examples especially from this guy. What is the alternative?
3. If yes, is there a documentation?
When talking about continuations, you’ll have to distinguish between two different kinds of them:
First-class continuations – Continuation-support that is deeply integrated in the language (Scheme or Ruby). Clojure does not support first-class continuations.
Continuation-passing-style (CPS) – CPS is just a style of coding and any language supporting anonymous functions will allow this style (which applies to Clojure too).
Examples:
-- Standard function
double :: Int -> Int
double x = 2 * x
-- CPS-function – We pass the continuation explicitly
doubleCPS :: Int -> (Int -> res) -> res
doubleCPS x cont = cont (2 * x)
; Call
print (double 2)
; Call CPS: Continue execution with specified anonymous function
double 2 (\res -> print res)
Read continuation on Wikipedia.
I don’t think that continuations are necessary for a good language, but especially first-class continuations and CPS in functional languages like Haskell can be quite useful (intelligent backtracking example).
I've written a Clojure port of cl-cont which adds continuations to Common Lisp.
https://github.com/swannodette/delimc
Abstract Continuations
Continuations are an abstract notion that are used to describe control flow semantics. In this sense, they both exist and don't exist (remember, they're abstract) in any language that offers control operators (as any Turing complete language must), in the same way that numbers both exist (as abstract entities) and don't exist (as tangible entities).
Continuations describe control effects such as function call/return, exception handling, and even gotos. A well founded language will, among other things, be designed with abstractions that are built on continuations (e.g., exceptions). (That is to say, a well-founded language will consist of control operators that were designed with continuations in mind. It is, of course, perfectly reasonable for a language to expose continuations as the only control abstraction, allowing users to build their own abstractions on top.)
First Class Continuations
If the notion of a continuation is reified as a first-class object in a language, then we have a tool upon which all kinds of control effects can be built. For example, if a language has first-class continuations, but not exceptions, we can construct exceptions on top of continuations.
Problems with First-Class Continuations
While first-class continuations are a powerful and useful tool in many cases, there are also some drawbacks to exposing them in a language:
Different abstractions built on top of continuations may result in unexpected / unintuitive behavior when composed. For example, a finally block might be skipped if I use a continuation to abort a computation.
If the current continuation may be requested at any time, then the language run-time must be structured so that it is possible to produce some data-structure representation of the current continuation at any time. This places some degree of burden on the run-time for a feature which, for better or worse, is often considered "exotic". If the language is hosted (such as Clojure is hosted on the JVM), then that representation must be able to fit within the framework provided by the hosting platform. There may also be other features a language would like to maintain (e.g., C interop) which restrict the solution space. Issues such as these increase the potential of an "impedence mismatch", and can severely complicate development of a performant solution.
Adding First-Class Continuations to a Language
Through metaprogramming, it is possible to add support for first-class continuations to a language. Generally, this approach involves transforming code to continuation-passing style (CPS), in which the current continuation is passed around as an explicit argument to each function.
For example, David Nolen's delimc library implements delimited continuations of portions of a Clojure program through a series of macro transforms. In a similar vein, I have authored pulley.cps, which is a macro compiler that transforms code into CPS, along with a run-time library to support more core Clojure features (such as exception handling) as well as interop with native Clojure code.
One issue with this approach is how you handle the boundary between native (Clojure) code and transformed (CPS) code. Specifically, since you can't capture the continuation of native code, you need to either disallow (or somehow restrict) interop with the base language or place a burden on the user of ensuring the context will allow any continuation they wish to capture to actually be captured.
pulley.cps tends towards the latter, although some attempts have been made to allow the user to manage this. For instance, it is possible to disallow CPS code to call into native code. In addition, a mechanism is provided to supply CPS versions of existing native functions.
In a language with a sufficiently strong type system (such as Haskell), it is possible to use the type system to encapsulate computations which might use control operations (i.e., continuations) from functionally pure code.
Summary
We now have the information necessary to directly answer your three questions:
Clojure does not support first-class continuations due to practical considerations.
All languages are built on continuations in the theoretical sense, but few languages expose continuations as first-class objects. However, it is possible to add continuations to any language via, e.g., transformation into CPS.
Check out the documentation for delimc and/or pulley.cps.
Is continuation a necessary feature in a language?
No. Plenty of languages don't have continuations.
If no, dont you need continuations? I have seen a lot of good examples especially from this guy. What is the alternative?
A call stack
A common use of continuations is in the implementation of control structures for: returning from a function, breaking from a loop, exception handling etc. Most languages (like Java, C++ etc) provide these features as part of the core language. Some languages don't (e.g: Scheme). Instead, these languages expose continuatiions as first class objects and let the programmer define new control structures. Thus Scheme should be looked upon as a programming language toolkit, not a complete language in itself.
In Clojure, we almost never need to use continuations directly, because almost all the control structures are provided by the language/VM combination. Still, first class continuations can be a powerful tool in the hands of the competent programmer. Especially in Scheme, continuations are better than the equivalent counterparts in other languages (like the setjmp/longjmp pair in C). This article has more details on this.
BTW, it will be interesting to know how Rich Hickey justifies his opinion about continuations. Any links for that?
Clojure (or rather clojure.contrib.monads) has a continuation monad; here's an article that describes its usage and motivation.
Well... Clojure's -> implements what you are after... But with a macro instead
I recently read a discussion regarding whether managed languages are slower (or faster) than native languages (specifically C# vs C++). One person that contributed to the discussion said that the JIT compilers of managed languages would be able to make optimizations regarding references that simply isn't possible in languages that use pointers.
What I'd like to know is what kind of optimizations that are possible on references and not on pointers?
Note that the discussion was about execution speed, not memory usage.
In C++ there are two advantages of references related to optimization aspects:
A reference is constant (refers to the same variable for its whole lifetime)
Because of this it is easier for the compiler to infer which names refer to the same underlying variables - thus creating optimization opportunities. There is no guarantee that the compiler will do better with references, but it might...
A reference is assumed to refer to something (there is no null reference)
A reference that "refers to nothing" (equivalent to the NULL pointer) can be created, but this is not as easy as creating a NULL pointer. Because of this the check of the reference for NULL can be omitted.
However, none of these advantages carry over directly to managed languages, so I don't see the relevance of that in the context of your discussion topic.
There are some benefits of JIT compilation mentioned in Wikipedia:
JIT code generally offers far better performance than interpreters. In addition, it can in some or many cases offer better performance than static compilation, as many optimizations are only feasible at run-time:
The compilation can be optimized to the targeted CPU and the operating system model where the application runs. For example JIT can choose SSE2 CPU instructions when it detects that the CPU supports them. With a static compiler one must write two versions of the code, possibly using inline assembly.
The system is able to collect statistics about how the program is actually running in the environment it is in, and it can rearrange and recompile for optimum performance. However, some static compilers can also take profile information as input.
The system can do global code optimizations (e.g. inlining of library functions) without losing the advantages of dynamic linking and without the overheads inherent to static compilers and linkers. Specifically, when doing global inline substitutions, a static compiler must insert run-time checks and ensure that a virtual call would occur if the actual class of the object overrides the inlined method.
Although this is possible with statically compiled garbage collected languages, a bytecode system can more easily rearrange memory for better cache utilization.
I can't think of something related directly to the use of references instead of pointers.
In general speak, references make it possible to refer to the same object from different places.
A 'Pointer' is the name of a mechanism to implement references. C++, Pascal, C... have pointers, C++ offers another mechanism (with slightly other use cases) called 'Reference', but essentially these are all implementations of the general referencing concept.
So there is no reason why references are by definition faster/slower than pointers.
The real difference is in using a JIT or a classic 'up front' compiler: the JIT can data take into account that aren't available for the up front compiler. It has nothing to do with the implementation of the concept 'reference'.
Other answers are right.
I would only add that any optimization won't make a hoot of difference unless it is in code where the program counter actually spends much time, like in tight loops that don't contain function calls (such as comparing strings).
An object reference in a managed framework is very different from a passed reference in C++. To understand what makes them special, imagine how the following scenario would be handled, at the machine level, without garbage-collected object references: Method "Foo" returns a string, which is stored into various collections and passed to different pieces of code. Once nothing needs the string any more, it should be possible to reclaim all memory used in storing it, but it's unclear what piece of code will be the last one to use the string.
In a non-GC system, every collection either needs to have its own copy of the string, or else needs to hold something containing a pointer to a shared object which holds the characters in the string. In the latter situation, the shared object needs to somehow know when the last pointer to it gets eliminated. There are a variety of ways this can be handled, but an essential common aspect of all of them is that shared objects need to be notified when pointers to them are copied or destroyed. Such notification requires work.
In a GC system by contrast, programs are decorated with metadata to say which registers or parts of a stack frame will be used at any given time to hold rooted object references. When a garbage collection cycle occurs, the garbage collector will have to parse this data, identify and preserve all live objects, and nuke everything else. At all other times, however, the processor can copy, replace, shuffle, or destroy references in any pattern or sequence it likes, without having to notify any of the objects involved. Note that when using pointer-use notifications in a multi-processor system, if different threads might copy or destroy references to the same object, synchronization code will be required to make the necessary notification thread-safe. By contrast, in a GC system, each processor may change reference variables at any time without having to synchronize its actions with any other processor.