The concept of lambdas (anonymous functions) is very clear to me. And I'm aware of polymorphism in terms of classes, with runtime/dynamic dispatch used to call the appropriate method based on the instance's most derived type. But how exactly can a lambda be polymorphic? I'm yet another Java programmer trying to learn more about functional programming.
You will observe that I don't talk about lambdas much in the following answer. Remember that in functional languages, any function is simply a lambda bound to a name, so what I say about functions translates to lambdas.
Polymorphism
Note that polymorphism doesn't really require the kind of "dispatch" that OO languages implement through derived classes overriding virtual methods. That's just one particular kind of polymorphism, subtyping.
Polymorphism itself simply means a function allows not just for one particular type of argument, but is able to act accordingly for any of the allowed types. The simplest example: you don't care for the type at all, but simply hand on whatever is passed in. Or, to make it not quite so trivial, wrap it in a single-element container. You could implement such a function in, say, C++:
template<typename T> std::vector<T> wrap1elem( T val ) {
return std::vector(val);
}
but you couldn't implement it as a lambda, because C++ (time of writing: C++11) doesn't support polymorphic lambdas.
Untyped values
...At least not in this way, that is. C++ templates implement polymorphism in rather an unusual way: the compiler actually generates a monomorphic function for every type that anybody passes to the function, in all the code it encounters. This is necessary because of C++' value semantics: when a value is passed in, the compiler needs to know the exact type (its size in memory, possible child-nodes etc.) in order to make a copy of it.
In most newer languages, almost everything is just a reference to some value, and when you call a function it doesn't get a copy of the argument objects but just a reference to the already-existing ones. Older languages require you to explicitly mark arguments as reference / pointer types.
A big advantage of reference semantics is that polymorphism becomes much easier: pointers always have the same size, so the same machine code can deal with references to any type at all. That makes, very uglily1, a polymorphic container-wrapper possible even in C:
typedef struct{
void** contents;
int size;
} vector;
vector wrap1elem_by_voidptr(void* ptr) {
vector v;
v.contents = malloc(sizeof(&ptr));
v.contents[0] = ptr;
v.size = 1;
return v;
}
#define wrap1elem(val) wrap1elem_by_voidptr(&(val))
Here, void* is just a pointer to any unknown type. The obvious problem thus arising: vector doesn't know what type(s) of elements it "contains"! So you can't really do anything useful with those objects. Except if you do know what type it is!
int sum_contents_int(vector v) {
int acc = 0, i;
for(i=0; i<v.size; ++i) {
acc += * (int*) (v.contents[i]);
}
return acc;
}
obviously, this is extremely laborious. What if the type is double? What if we want the product, not the sum? Of course, we could write each case by hand. Not a nice solution.
What would we better is if we had a generic function that takes the instruction what to do as an extra argument! C has function pointers:
int accum_contents_int(vector v, void* (*combine)(int*, int)) {
int acc = 0, i;
for(i=0; i<v.size; ++i) {
combine(&acc, * (int*) (v.contents[i]));
}
return acc;
}
That could then be used like
void multon(int* acc, int x) {
acc *= x;
}
int main() {
int a = 3, b = 5;
vector v = wrap2elems(a, b);
printf("%i\n", accum_contents_int(v, multon));
}
Apart from still being cumbersome, all the above C code has one huge problem: it's completely unchecked if the container elements actually have the right type! The casts from *void will happily fire on any type, but in doubt the result will be complete garbage2.
Classes & Inheritance
That problem is one of the main issues which OO languages solve by trying to bundle all operations you might perform right together with the data, in the object, as methods. While compiling your class, the types are monomorphic so the compiler can check the operations make sense. When you try to use the values, it's enough if the compiler knows how to find the method. In particular, if you make a derived class, the compiler knows "aha, it's ok to call that method from the base class even on a derived object".
Unfortunately, that would mean all you achieve by polymorphism is equivalent to compositing data and simply calling the (monomorphic) methods on a single field. To actually get different behaviour (but controlledly!) for different types, OO languages need virtual methods. What this amounts to is basically that the class has extra fields with pointers to the method implementations, much like the pointer to the combine function I used in the C example – with the difference that you can only implement an overriding method by adding a derived class, for which the compiler again knows the type of all the data fields etc. and you're safe and all.
Sophisticated type systems, checked parametric polymorphism
While inheritance-based polymorphism obviously works, I can't help saying it's just crazy stupid3 sure a bit limiting. If you want to use just one particular operation that happens to be not implemented as a class method, you need to make an entire derived class. Even if you just want to vary an operation in some way, you need to derive and override a slightly different version of the method.
Let's revisit our C code. On the face of it, we notice it should be perfectly possible to make it type-safe, without any method-bundling nonsense. We just need to make sure no type information is lost – not during compile-time, at least. Imagine (Read ∀T as "for all types T")
∀T: {
typedef struct{
T* contents;
int size;
} vector<T>;
}
∀T: {
vector<T> wrap1elem(T* elem) {
vector v;
v.contents = malloc(sizeof(T*));
v.contents[0] = &elem;
v.size = 1;
return v;
}
}
∀T: {
void accum_contents(vector<T> v, void* (*combine)(T*, const T*), T* acc) {
int i;
for(i=0; i<v.size; ++i) {
combine(&acc, (*T) (v[i]));
}
}
}
Observe how, even though the signatures look a lot like the C++ template thing on top of this post (which, as I said, really is just auto-generated monomorphic code), the implementation actually is pretty much just plain C. There are no T values in there, just pointers to them. No need to compile multiple versions of the code: at runtime, the type information isn't needed, we just handle generic pointers. At compile time, we do know the types and can use the function head to make sure they match. I.e., if you wrote
void evil_sumon (int* acc, double* x) { acc += *x; }
and tried to do
vector<float> v; char acc;
accum_contents(v, evil_sumon, acc);
the compiler would complain because the types don't match: in the declaration of accum_contents it says the type may vary, but all occurences of T do need to resolve to the same type.
And that is exactly how parametric polymorphism works in languages of the ML family as well as Haskell: the functions really don't know anything about the polymorphic data they're dealing with. But they are given the specialised operators which have this knowledge, as arguments.
In a language like Java (prior to lambdas), parametric polymorphism doesn't gain you much: since the compiler makes it deliberately hard to define "just a simple helper function" in favour of having only class methods, you can simply go the derive-from-class way right away. But in functional languages, defining small helper functions is the easiest thing imaginable: lambdas!
And so you can do incredible terse code in Haskell:
Prelude> foldr (+) 0 [1,4,6]
11
Prelude> foldr (\x y -> x+y+1) 0 [1,4,6]
14
Prelude> let f start = foldr (\_ (xl,xr) -> (xr, xl)) start
Prelude> :t f
f :: (t, t) -> [a] -> (t, t)
Prelude> f ("left", "right") [1]
("right","left")
Prelude> f ("left", "right") [1, 2]
("left","right")
Note how in the lambda I defined as a helper for f, I didn't have any clue about the type of xl and xr, I merely wanted to swap a tuple of these elements which requires the types to be the same. So that would be a polymorphic lambda, with the type
\_ (xl, xr) -> (xr, xl) :: ∀ a t. a -> (t,t) -> (t,t)
1Apart from the weird explicit malloc stuff, type safety etc.: code like that is extremely hard to work with in languages without garbage collector, because somebody always needs to clean up memory once it's not needed anymore, but if you didn't watch out properly whether somebody still holds a reference to the data and might in fact need it still. That's nothing you have to worry about in Java, Lisp, Haskell...
2There is a completely different approach to this: the one dynamic languages choose. In those languages, every operation needs to make sure it works with any type (or, if that's not possible, raise a well-defined error). Then you can arbitrarily compose polymorphic operations, which is on one hand "nicely trouble-free" (not as trouble-free as with a really clever type system like Haskell's, though) but OTOH incurs quite a heavy overhead, since even primitive operations need type-decisions and safeguards around them.
3I'm of course being unfair here. The OO paradigm has more to it than just type-safe polymorphism, it enables many things e.g. old ML with it's Hindler-Milner type system couldn't do (ad-hoc polymorphism: Haskell has type classes for that, SML has modules), and even some things that are pretty hard in Haskell (mainly, storing values of different types in a variable-size container). But the more you get accustomed to functional programming, the less need you will feel for such stuff.
In C++ polymorphic (or generic) lambda starting from C++14 is a lambda that can take any type as an argument. Basically it's a lambda that has auto parameter type:
auto lambda = [](auto){};
Is there a context that you've heard the term "polymorphic lambda"? We might be able to be more specific.
The simplest way that a lambda can be polymorphic is to accept arguments whose type is (partly-)irrelevant to the final result.
e.g. the lambda
\(head:tail) -> tail
has the type [a] -> [a] -- e.g. it's fully-polymorphic in the inner type of the list.
Other simple examples are the likes of
\_ -> 5 :: Num n => a -> n
\x f -> f x :: a -> (a -> b) -> b
\n -> n + 1 :: Num n => n -> n
etc.
(Notice the Num n examples which involve typeclass dispatch)
Related
Some languages have ways of tracking properties like purity or the presence/absence of exceptions at compile time. The fact that they do this sort of resembles an effect system.
There seem to be two broad categories of these effect-like systems, one in which any value can be "wrapped in an effect" like the IO Monad in Haskell and one in which only functions can be annotated (like noexcept in C++ or checked exceptions in Java). constexpr in C++ is a weird case that I don't know how to think about and am intentionally ignoring, since it means very different things when applied to a function and a non-function value.
I'm wondering what you call the Haskell-IO-Monad style of effect tracking vs the checked-exception style. It seems like the only reason you would use the latter is backwards compatibility with a language that doesn't track the effect you're interested in.
More explicitly, Haskell tracks effects at the type level, via the IO Monad.
a -> b is the type of a pure function with no side effects.
Impure functions return an IO value, which has a special status within the runtime / semantics of Haskell.
The nearest equivalent to an impure function in other languages would be something like
a -> IO b or, said differently, a computation producing a b parameterized by a. The type constructor -> still has the same meaning of pure function that it did before.
C++ has a distinction between functions that can potentially throw exceptions (the default) and functions that can't.
int add(int x, int y) noexcept {
return x + y;
}
However it isn't possible to mark a non-function as noexcept.
// BAD!
int x noexcept = <expr>;
The noexcept applies to the function itself, in effect giving you a different type constructor than ->.
I have learned Haskell for about one year, and came up with a question that could the talented compiler writers add a new feature called "subset" by me to enhance the Haskell's type system to catch many errors including IOExceptions in compiling stage. I'm novice of Theory of types, and forgive my wishful thinking.
My initial purpose is not how to solve the problem but to know whether there exists a related solution but because of some reasons the solution is not introduced to Haskell.
Haskell is nearly perfect in my mind except for some little things, and I will express my wish to Haskell of the future in the following lines.
The following is the major one:
If we can define a type, which is just a "subset" of Int assuming Haskell allows us to do that, like bellow:
data IntNotZero = Int {except `0`} -- certainly it is not legal in Haskell, but I just assume that Haskell allows us to define a type as a "subset" of an already existing type. I'm novice of Theory of types, and forgive me.
And If a function needs a parameter of Int, a variable of IntNotZero, which is just a "subset" of Int, can also be a parameter of the function. But, If a function needs a IntNotZero, then a Int is illegal.
For example:
div' :: Int -> IntNotZero -> Int
div' = div
aFunction :: Int -> Int -> Int --If we casually write it, then the compiler will complain for type conflict.
aFunction = div'
aFunction2 :: Int -> Int -> Int --we have to distinguish between `Int` and `IntNotZero`.
aFunction2 m n = type n of --An assumed grammar like `case ... of` to separate "subset" from its complement. `case ...of` only works on different patterns.
IntNotZero -> m `div` n
otherwise -> m + n
For a more useful example:
data HandleNotClosed = Handle {not closed} --this type infers a Handle not closed
hGetContents' :: HandleNotClosed -> IO String --this function needs a HandleNotClosed and a Handle will make a type conflict.
hGetContents' = hGetContents
wrongMain = do
...
h <- openFile "~/xxx/aa" ReadMode
... -- we do many tasks with h and we may casually closed h
contents <- hGetContents' h --this will raise a type conflict, because h has type of Handle not HandleNotClosed.
...
rightMain = do
...
h <- openFile "~/xxx/aa" ReadMode
... -- we do many tasks with h and we may casually closed h
type h of -- the new grammar.
HandleNotClosed -> do
contents <- hGetContents' h
...
otherwise -> ...
If we combine ordinary IO with Exception to a new "supset", then we may get free of IOErrors.
What you want sounds similar to "refinement types" à la Liquid Haskell. This is an external tool that allows you to "refine" your Haskell types by specifying additional predicates that hold over your types. To check that these hold, you use an SMT solver to verify all the constraints have been satisfied.
The following code snippets are taken from their introductory blog post.
For example, you could write the type that zero is 0:
{-# zero :: { v : Int | v = 0 } #-}
zero :: Int
zero = 0
You'll notice that the syntax for types looks just like set notation for math--you're defining a new type as a subset of the old on. In this case, you're defining the type of Ints that are equal to 0.
You can use this system to write a safe divide function:
{-# divide :: Int -> { v : Int | v != 0 } -> Int #-}
divide :: Int -> Int -> Int
divide n 0 = error "Cannot divide by 0."
divide n d = n `div` d
When you actually try to compile this program, Liquid Haskell will see that having 0 as the denominator violates the predicate and so the call to error cannot happen. Moreover, when you try to use divide, it will check that the argument you pass in cannot be 0.
Of course, to make this useful, you have to be able to add information about the postconditions of your functions, not just the preconditions. You can just do this by refining the result type of the function; for example, you can imagine the following type for abs:
{-# abs :: Int -> { v : Int | 0 <= v } #-}
Now the type system knows that the result of calling abs will never be negative, and it can take advantage of this fact when it needs to verify your program.
Like other people mentioned, using this sort of type system means you will have to have proofs in your code. The advantage of Liquid Haskell is that it uses an SMT solver to generate the proof for you automatically--you just have to write the assertions.
Liquid Haskell is still a research project, and it's limited by what can reasonably be done with an SMT solver. I haven't used it myself, but it looks really awesome and seems to be exactly what you want. One thing I'm not sure about is how it interacts with custom types and IO--something you might want to look into yourself.
The problem is that the compiler can't determine if something is of type IntNotZero. For example:
f :: Int -> IntNotZero
f x = someExtremlyComplexComputation
the compiler would have to prove that someExtremlyComplexComputation doesn't produce a zero result, which is in general impossible.
One way how to approach this is in plain Haskell to create a module that hides the representation of IntNotZero and publishes only a smart constructor such as
module MyMod (IntNotZero(), intNotZero) where
newtype IntNotZero = IntNotZero Int
intNotZero :: Int -> IntNotZero
intNotZero 0 = error "Zero argument"
intNotZero x = IntNotZero x
-- etc
The obvious drawback is that the constraint is checked only at runtime.
There are more complex systems than Haskell that use Dependent types. These are types that depend on values, and they allow you to express just what you want. Unfortunately these systems are rather complex and not much widespread. If you're interested in the subject, I suggest you to read Certified Programming with Dependent Types by Adam Chlipala.
We have types and we have values. A Type is a set of (infinite) values. String is a type and all the possible string values are part of the String set. Now, the most important distinction about types and values is this - Types are about compile time and Values are available at runtime.
If we look at you first example which talks about a new type which is subtype (or subset) of Int type such that "the value of the Int can't be zero", which means you want to define a type which put some restrictions on a value BUT types are compile time and values are runtime things - a compile time thing can't restrict a runtime thing because the runtime thing is not there yet for compile time thing to consume.
Similarly the handle value is a runtime thing and only at runtime you can know if it is closed or not and for that you have functions to check whether the handle is closed or not.
IO is all about runtime and you can't use a type system to get free of IOErrors.
For modeling runtime failures you can use data types like Maybe or Either to indicate that the function may not be able to do what is was supposed to do and as these data types implements functors, moands and other computations patterns you can easily compose them.
A type system is more of a structuring/design tool which make things more explicit and clear and makes you think more about your design but it can't do what functions are supposed to do.
The film is : Typed Lambda calculus. Lambda in lead role, Typed in supporting role :)
To expand on #augustss's comment, it's quite possible using dependently typed languages. Haskell is not exactly dependently typed, but it's close enough that dependent types can be "faked".
People don't today commonly use dependent types for several reasons
they're still very much research topics
they complicate and weaken type inference
they're somewhat more difficult to use in some circumstances
they can cause type-checking to take much longer or even fail to terminate, and
it's just more difficult to create production dependently-typed compilers.
That said, proponents of dependent typing find the error reduction you're looking for quite tenable. They also anticipate better safety and faster compiled binaries. Finally, dependently typed systems can be used as "proof systems" for mathematics. For a very current example consider the "Homotopy Type Theory" Agda code which formally proves many of the assertions of a new field of dependent typing math.
For a taste of dependent typing you can read/explore either Pierce's Software Foundations or Chlipala's Certified Programming with Dependent Types.
With dependent types you might introduce a type like this
div :: Int -> (x :: Int) -> (Inequal x 0) -> Int
where the second argument introduces a dependency of the type on the actual value of the argument and the third argument demands a proof of the proposition that x /= 0. With such a proof in hand (so long as nobody cheats and uses undefined as that proof) it's possible to feel confident dividing by the second argument could never be undefined.
The challenge comes from creating (automatically or manually) a value to pass in as the third argument. For such a simple example it may not be too difficult, but it becomes possible to encode demands for proofs that are very difficult to generate, or even impossible.
As an example of another advantage, consider
fold1 :: (f :: a -> a -> a) -> Associative f -> [a] -> a
which, ignoring the second argument, is just a regular fold. The second argument could be a proof that f associates and thus allows us to use a tree-like merging algorithm with log complexity instead of linear. But, in order to "prove" Associative we need to embed a theory of application and association into our types and have the competency to create proofs within it.
Simpler invariants exist such as the all-prevalent Vec type of "fixed-length vectors". These are lists where the length of the list (a value) is included in the type allowing us to have nice things like
(++) :: Vec n a -> Vec m a -> Vec (n + m) a
which, if we have some good theories of addition (or, more generally, magmas, monoids, and groups) in our type system then it isn't too difficult to create our result type which holds information about the way lengths of Vecs interact under concatenation.
Basically, I wonder if a language exists where this code will be invalid because even though counter and distance are both int under the hood, they represent incompatible types in the real world:
#include <stdio.h>
typedef int counter;
typedef int distance;
int main() {
counter pies = 1;
distance lengthOfBiscuit = 4;
printf("total pies: %d\n", pies + lengthOfBiscuit);
return 0;
}
That compiles with no warnings with "gcc -pedantic -Wall" and all other languages where I've tried it. It seems like it would be a good idea to disallow accidentally adding a counter and a distance, so where is the language support?
(Incidentally, the real-life example that prompted this quesion was web dev work in PHP and Python -- I was trying to make "HTML-escaped string", "SQL-escaped string" and "raw dangerous user input" incompatible, but the best I can seem to get is apps hungarian notation as suggested here --> http://www.joelonsoftware.com/articles/Wrong.html <-- and that still relies on human checking ("wrong code looks wrong") rather than compiler support ("wrong code is wrong"))
Haskell can do this, with GeneralizedNewtypeDeriving you can treat wrapped values as the underlying thing, whilst only exposing what you need:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Counter = Counter Int deriving Num
newtype Distance = Distance Int deriving Num
main :: IO ()
main = print $ Counter 1 + Distance 2
Now you get the error:
Add.hs:6:28:
Couldn't match expected type ‘Counter’ with actual type ‘Distance’
In the second argument of ‘(+)’, namely ‘Distance 2’
In the second argument of ‘($)’, namely ‘Counter 1 + Distance 2’
You can still "force" the underlying data type with "coerce", or by unwrapping the Ints explicitly.
I should add that any language with "real" types should be able to do this.
In Ada you can have types that use the same representation, but are still distinct types. What a "strong typedef" would be (if it existed) in C or C++.
In your case, you could do
type counter is new Integer;
type distance is new Integer;
to create two new types that behave like integers, but cannot be mixed.
Derived types and sub types in Ada
You could ccreate an object wrapping the undelying type in a member variable and define operations (even in the form of functions) that make sense on that type (e.g. LEngth would define "plus" allowing addition to another length, but for angle).
A drawback of this approach is you have to create a wrapper for each underlying type you care about and define the appropriate operations for each sensible combination, which might be tedious and possibly error-prone.
In C++, you could check out BOOST support for dimensions. The example given is designed primarily for physical dimensions, but I think you could adapt it to many others as well.
Imagine a simple (made up) language where functions look like:
function f(a, b) = c + 42
where c = a * b
(Say it's a subset of Lisp that includes 'defun' and 'let'.)
Also imagine that it includes immutable objects that look like:
struct s(a, b, c = a * b)
Again analogizing to Lisp (this time a superset), say a struct definition like that would generate functions for:
make-s(a, b)
s-a(s)
s-b(s)
s-c(s)
Now, given the simple set up, it seems clear that there is a lot of similarity between what happens behind the scenes when you either call 'f' or 'make-s'. Once 'a' and 'b' are supplied at call/instantiate time, there is enough information to compute 'c'.
You could think of instantiating a struct as being like a calling a function, and then storing the resulting symbolic environment for later use when the generated accessor functions are called. Or you could think of a evaluting a function as being like creating a hidden struct and then using it as the symbolic environment with which to evaluate the final result expression.
Is my toy model so oversimplified that it's useless? Or is it actually a helpful way to think about how real languages work? Are there any real languages/implementations that someone without a CS background but with an interest in programming languages (i.e. me) should learn more about in order to explore this concept?
Thanks.
EDIT: Thanks for the answers so far. To elaborate a little, I guess what I'm wondering is if there are any real languages where it's the case that people learning the language are told e.g. "you should think of objects as being essentially closures". Or if there are any real language implementations where it's the case that instantiating an object and calling a function actually share some common (non-trivial, i.e. not just library calls) code or data structures.
Does the analogy I'm making, which I know others have made before, go any deeper than mere analogy in any real situations?
You can't get much purer than lambda calculus: http://en.wikipedia.org/wiki/Lambda_calculus. Lambda calculus is in fact so pure, it only has functions!
A standard way of implementing a pair in lambda calculus is like so:
pair = fn a: fn b: fn x: x a b
first = fn a: fn b: a
second = fn a: fn b: b
So pair a b, what you might call a "struct", is actually a function (fn x: x a b). But it's a special type of function called a closure. A closure is essentially a function (fn x: x a b) plus values for all of the "free" variables (in this case, a and b).
So yes, instantiating a "struct" is like calling a function, but more importantly, the actual "struct" itself is like a special type of function (a closure).
If you think about how you would implement a lambda calculus interpreter, you can see the symmetry from the other side: you could implement a closure as an expression plus a struct containing the values of all the free variables.
Sorry if this is all obvious and you just wanted some real world example...
Both f and make-s are functions, but the resemblance doesn't go much further. Applying f calls the function and executes its code; applying make-s creates a structure.
In most language implementations and modelizations, make-s is a different kind of object from f: f is a closure, whereas make-s is a constructor (in the functional languages and logic meaning, which is close to the object oriented languages meaning).
If you like to think in an object-oriented way, both f and make-s have an apply method, but they have completely different implementations of this method.
If you like to think in terms of the underlying logic, f and make-s have a type build on the samme type constructor (the function type constructor), but they are constructed in different ways and have different destruction rules (function application vs. constructor application).
If you'd like to understand that last paragraph, I recommend Types and Programming Languages by Benjamin C. Pierce. Structures are discussed in §11.8.
Is my toy model so oversimplified that it's useless?
Essentially, yes. Your simplified model basically boils down to saying that each of these operations involves performing a computation and putting the result somewhere. But that is so general, it covers anything that a computer does. If you didn't perform a computation, you wouldn't be doing anything useful. If you didn't put the result somewhere, you would have done work for nothing as you have no way to get the result. So anything useful you do with a computer, from adding two registers together, to fetching a web page, could be modeled as performing a computation and putting the result somewhere that it can be accessed later.
There is a relationship between objects and closures. http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html
The following creates what some might call a function, and others might call an object:
Taken from SICP ( http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-21.html )
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
dispatch)
Not having them used them all that much, I'm not quite sure about all the ways
lambda-definitions can be used (other than map/collect/do/lightweight local function syntax). For anyone interested in posting some examples:
provide explanations to help readers understand how lambda-definitions are being used;
preferred languages for the examples: Python, Smalltalk, Haskell.
You can make a functional data structure out of lambdas. Here is a simple one - a functional list (Python), supporting add and contains methods:
empty = lambda x : None
def add(lst, item) :
return lambda x : x == item or lst(x)
def contains(lst, item) :
return lst(item) or False
I just coded this quickly for fun - notice that you're not allowed to add any falsy values as is. It also is not tail-recursive, as a good functional structure should be. Exercises for the reader!
You can use them for control flow. For example, in Smalltalk, the "ifTrue:ifFalse:" method is a method on Boolean objects, with a different implementation on each of True and False classes. The expression
someBoolean ifTrue: [self doSomething] ifFalse: [self doSomethingElse]
uses two closures---blocks, in [square brackets] in Smalltalk syntax---one for the true branch, and one for the false branch. The implementation of "ifTrue:ifFalse:" for instances of class True is
ifTrue: block1 ifFalse: block2
^ block1 value
and for class False:
ifTrue: block1 ifFalse: block2
^ block2 value
Closures, here, are used to delay evaluation so that a decision about control flow can be taken, without any specialised syntax at all (besides the syntax for blocks).
Haskell is a little different, with its lazy evaluation model effectively automatically producing the effect of closures in many cases, but in Scheme you end up using lambdas for control flow a lot. For example, here is a utility to retrieve a value from an association-list, supplying an optionally-computed default in the case where the value is not present:
(define (assq/default key lst default-thunk)
(cond
((null? lst) (default-thunk)) ;; actually invoke the default-value-producer
((eq? (caar lst) key) (car lst))
(else (assq/default key (cdr lst) default-thunk))))
It would be called like this:
(assq/default 'mykey my-alist (lambda () (+ 3 4 5)))
The key here is the use of the lambda to delay computation of the default value until it's actually known to be required.
See also continuation-passing-style, which takes this to an extreme. Javascript, for instance, relies on continuation-passing-style and closures to perform all of its blocking operations (like sleeping, I/O, etc).
ETA: Where I've said closures above, I mean lexically scoped closures. It's the lexical scope that's key, often.
You can use a lambda to create a Y Combinator, that is a function that takes another function and returns a recursive form of it. Here is an example:
def Y(le):
def _anon(cc):
return le(lambda x: cc(cc)(x))
return _anon(_anon)
This is a thought bludgeon that deserves some more explanation, but rather than regurgitate it here check out this blog entry (above sample comes from there too).
Its C#, but I personally get a kick out of this article every time I read it:
Building Data out of Thin Air - an implementation of Lisp's cons, car, and cdr functions in C#. It shows how to build a simple stack data structure entirely out of lambda functions.
It isn't really quite the same concept as in haskell etc, but in C#, the lambda construct has (optionally) the ability to compile to an objcet model representing the code (expression-trees) rather than code itself (this is itself one of the cornerstones of LINQ).
This in turn can lead to some very expressive meta-programming opportunities, for example (where the lambda here is expressing "given a service, what do you want to do with it?"):
var client = new Client<ISomeService>();
string captured = "to show a closure";
var result = client.Invoke(
svc => svc.SomeMethodDefinedOnTheService(123, captured)
);
(assuming a suitable Invoke signature)
There are lots of uses for this type of thing, but I've used it to build an RPC stack that doesn't require any runtime code generation - it simply parses the expression-tree, figures out what the caller intended, translates it to RPC, invokes it, gathers the response, etc (discussed more here).
An example in Haskell to compute the derivative of a single variabled function using a numerical approximation:
deriv f = \x -> (f (x + d) - f x) / d
where
d = 0.00001
f x = x ^ 2
f' = deriv f -- roughly equal to f' x = 2 * x