www.madore.org/%7Edavid/programs/unlambda -> www.madore.org/~david/programs/unlambda/
This page is being revised in preparation of the Unlambda 3 distribution. Introduction "It's disgusting -- it's revolting -- we love it." CyberTabloid "Unlambda, the language in which every program is an IOUCC." Encyclopaedia Internetica "The worst thing to befall us since Intercal." Computer Languages Today "The effect of reading an Unlambda program is like habing your brains smashed out by a Lisp sexp wrapped around an ENIAC. You won't find anything like it west of Alpha Centauri." The Hitch-Hacker's Guide to Programming What is Unlambda?
below for links) are typically made nasty by either strongly restricting the set of allowed operations in the language, or making them very different from what programmers are used to, or both. But whereas most obfuscated programming languages try to somehow model the Turing Machine paradigm, Unlambda does not use a tape, array or stack. as a matter of fact, it does not manipulate integers in any way. Other remarkable (un)features of Unlambda are the fact that it does not have any variables, data structures or code constructs (such as loops, conditionals and such like). Rather, Unlambda uses a functional approach to programming: the only form of objects it manipulates are functions. Each function takes a function as argument and returns a function. Apart from a binary "apply" operation, Unlambda provides several builtin functions (the most important ones being the K and S combinators). User-defined functions can be created, but not saved or named, because Unlambda does not have any variables. Despite all these apparently unsurmountable limitations, Unlambda is fully Turing-equivalent. Mathematically, the core of the language can be described as an implementation of the lambda-calculus without the lambda operation, relying entirely on the K and S combinators. It uses head ("eager", "by value", "strict") evaluation. However, as far as I know, I am the first to have taken this theoretical concept and made it into an actual (deliberately obfuscated) programming language. I added a couple of functions (chosen for their obscurity) to the language so as to make output (and, in version 2, input) possible, or just to make things even more obscure (delay and call/cc are such).
Clean, which are lazy and demand explicit sequencing of side effects. I dislike this terminology: for one thing, a "functional" programming language is one in which functions have first-class citizenship, so a "purely functional" one should be one where, as in Unlambda, only functions have first-class citizenship. And what are usually called "purely functional programming languages" should be called, exactly as I just did, lazily evaluating programming languages with explicitly sequenced side effects. All these points are orthogonal: it is quite possible to conceive a lazy programming language which is not functional, or an eager (ie non-lazy) functional programming language which still demands explicit sequencing of side effects. In any case, this is to say that I might, on occasion, speak of Unlambda as a "purely functional" programming language, although, with the usual terminology, it is not. Well, let's discuss an example: the following Unlambda program calculates and prints the Fibonacci numbers (as lines of asterisks) sssiiki k*ssks sksksssksskskrsksikk ksksk (All whitespace is optional and arbitrary. Writing Unlambda programs isn't really as hard as it might seem; however, reading Unlambda programs is practically impossible.
explaining what all this means later on, but let's just stick to basic observations for the moment. As you can see, the most common character (essentially, it makes up half of any Unlambda program) is the backquote (ASCII number 96=0x60). After that come the S and K combinators (and I, but I can be done away with entirely). Some other characters can occur in Unlambda programs but they are not nearly so common. The more sophisticated Unlambda functions (v, d, c, e and the input functions) are not used here at all. The number one principle of the Unlambda language is that everything is a function: this is true in the sense that Unlambda is a profile of the pure untyped lambda calculus.
Because there is no abstraction, functions are not named in Unlambda (except the builtin ones): there are no variables or such thing. This doesn't mean you can't build up your own functions. Nor does the fact that there are only functions in Unlambda prevent you from coming up with data structures and the like, but you just have to represent them with ad hoc functions. In fact, you can so well build your own structures and such that Unlambda is (and, to work, must be) garbage-collected like any decent high-level language. To start with, you have the builtin functions (i, k, s and the like), and you can do one thing: apply a function F to a function G, the result being denoted FG.
List Sites Tutorial Although the very idea of a tutorial for such an obfuscated language as Unlambda is patently absurd, I shall try to give a brief introduction to the concepts before dwelling in the details of the reference section (which is also very short considering how small Unlambda is as a whole).
introduction, the only objects that the Unlambda programming language manipulates are functions. Every function takes exactly one argument (that is also a function) and returns one value (that is also a function). The basic building blocks for Unlambda programs are the primitive functions and the application operation. Function application is designated with the backquote (ASCII number 96=0x60) character. The notation is prefix, in other words, FG means F applied to G We'll be explaining in detail what application means exactly, but for the moment, we'll just say that it means that F will do something with the value of G, including applying other functions to it, or applying it to other functions. We have to note, of course, that both F and G may themselves be obtained by applying various functions to each other. The fact that every Unlambda function is unary (takes exactly one argument) means that the backquote notation is unambiguous, and we do not need parentheses (or, if you prefer, the backquote plays the role of the open parenthesis of Lisp, but the closed parenthesis is unnecessary). For example, FGH means (F applied to G) applied to H whereas FGH means F applied to (G applied to H). To check whether an expression is a valid Unlambda expression, there is a simple criterion: start at the left with a counter equal to the number 1, and move from left to right: for every backquote encountered, increment the counter, and for every primitive function encountered, decrement it; the counter must always remain positive except at the very end when it must reach zero. Since all Unlambda functions take exactly one argument, when we wish to handle a function of several arguments, it is necessary to "curry" that function.
The idea being that F will do nothing but read the first argument and return (without side effects) a function that reads the second argument and returns a function that reads the third argument and finally do whatever calculation it is F was supposed to perform.
are legal, but they don't do much except wait for more arguments to come. Of course, when the user is defining his own functions, he may use whatever mechanism he seems fit for reading the functions' arguments (but such a currying is certainly the best because pairs and lists are so horribly difficult to define in Unlambda). But the builtin k and s functions take respectively 2 and 3 arguments, and the several arguments are passed in the manner which we have just described.
This is what the Java version of the Unlambda interpreter does, for example (whereas the Scheme version does not). Combinators The k and s builtins are the core of the language.
It takes three arguments, X, Y and Z, and evaluates as does XZYZ. So, let's get things straight: k doesn't do much until it is applied to two arguments, in which case it throws the second one away and returns the first. As for s, it doesn't do much until it is applied to three arguments, at which point it applies the first to the third,...
|