33

I'm currently doing a Functional Programming course and I'm quite amused by the concept of higher-order functions and functions as first class citizens. However, I can't yet think of many practically useful, conceptually amazing, or just plain interesting higher-order functions. (Besides the typical and rather dull map, filter, etc functions).

Do you know examples of such interesting functions?

Maybe functions that return functions, functions that return lists of functions (?), etc.

I'd appreciate examples in Haskell, which is the language I'm currently learning :)

Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
Manuel Araoz
  • 15,962
  • 24
  • 71
  • 95

14 Answers14

45

Well, you notice that Haskell has no syntax for loops? No while or do or for. Because these are all just higher-order functions:

 map :: (a -> b) -> [a] -> [b]

 foldr :: (a -> b -> b) -> b -> [a] -> b

 filter :: (a -> Bool) -> [a] -> [a]

 unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

 iterate :: (a -> a) -> a -> [a]

Higher-order functions replace the need for baked in syntax in the language for control structures, meaning pretty much every Haskell program uses these functions -- making them quite useful!

They are the first step towards good abstraction because we can now plug custom behavior into a general purpose skeleton function.

In particular, monads are only possible because we can chain together, and manipulate functions, to create programs.

The fact is, life is pretty boring when it is first-order. Programming only gets interesting once you have higher-order.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
  • 3
    Well, monads follow, and then higher order functions over monads follow from that. It is turtles all the way! – Don Stewart Apr 26 '11 at 19:53
  • 4
    Very nice answer. Some of the "control structures" people have come up with are quite unconventional but spectacularly useful. A great example that comes to my mind is `quickCheck` - a control operator that runs the supplied function several times with arbitrary inputs attempting to make it fail. It's such a simple concept, and higher-order functions (and type classes) make it equally simple to use. Just `quickCheck $ \n xs -> length (take n xs) <= n` and watch (that one's false, BTW, for negative `n` - an easy oversight to make, but quickCheck catches it easily). – mokus Apr 27 '11 at 13:49
  • 2
    Another one that just came to mind (it didn't immediately because I tend to take it for granted) is 'forkIO', or anything else having to do with concurrency. Spawning a thread is such a useful higher order function that people even use it in C, despite the language's lack of any special support for them. Just look at the type of, say, `pthread_create`. HOFs are so useful that people are willing to put up with defining parameter structs, fiddling with pointers to them, usually manually managing the memory for them, and casting to and from `void *`, just to be able to use them in C. – mokus Apr 29 '11 at 14:42
  • "life is boring when it's first order." Well, when you desugar an imperative language's control structure through it's semantics, you end up with something higher order, but you're *hard wired* into a certain instantiation of the control structure (namely, the range of primitives which the language provides). Higher order behavior lets you grab this *directly*. And (as you know), you can *prove* that anything that can be done with loops can be done with HOFs, making higher order programming an extension of (typical) imperative programming. – Kristopher Micinski Sep 25 '12 at 01:15
37

Many techniques used in OO programming are workarounds for the lack of higher order functions.

This includes a number of the design patterns that are ubiquitous in functional programming. For example, the visitor pattern is a rather complicated way to implement a fold. The workaround is to create a class with methods and pass in an element of the class in as an argument, as a substitute for passing in a function.

The strategy pattern is another example of a scheme that often passes objects as arguments as a substitute for what is actually intended, functions.

Similarly dependency injection often involves some clunky scheme to pass a proxy for functions when it would often be better to simply pass in the functions directly as arguments.

So my answer would be that higher-order functions are often used to perform the same kinds of tasks that OO programmers perform, but directly, and with a lot less boilerplate.

sigfpe
  • 7,996
  • 2
  • 27
  • 48
  • 7
    Just as a side note, there is nothing about OO the prevents functions as first-class objects. Both SmallTalk and Ruby have first-class functions. – John F. Miller May 03 '11 at 05:46
  • @John Absolutely. My first introduction to functions as first class objects was through learning Smalltalk many years ago. – sigfpe May 17 '11 at 17:22
15

I really started to feel the power when I learned a function can be part of a data structure. Here is a "consumer monad" (technobabble: free monad over (i ->)).

data Coro i a
    = Return a
    | Consume (i -> Coro i a)

So a Coro can either instantly yield a value, or be another Coro depending on some input. For example, this is a Coro Int Int:

Consume $ \x -> Consume $ \y -> Consume $ \z -> Return (x+y+z)

This consumes three integer inputs and returns their sum. You could also have it behave differently according to the inputs:

sumStream :: Coro Int Int
sumStream = Consume (go 0)
    where
    go accum 0 = Return accum
    go accum n = Consume (\x -> go (accum+x) (n-1))

This consumes an Int and then consumes that many more Ints before yielding their sum. This can be thought of as a function that takes arbitrarily many arguments, constructed without any language magic, just higher order functions.

Functions in data structures are a very powerful tool that was not part of my vocabulary before I started doing Haskell.

luqui
  • 59,485
  • 12
  • 145
  • 204
  • 1
    Its `Return accum` in the first branch. Also, I find it a bit dirty that the first consumed element as a semantic so different from the other ones. I would rather have made the length a parameter of `subStream`. – gasche Apr 26 '11 at 20:03
  • @gasche, thanks fixed. Also I agree that it's dirty -- the point was that the "signature" of this "function" can depend on its parameters, showing the power of a function-in-data-structure construction. – luqui Apr 27 '11 at 00:02
11

Check out the paper 'Even Higher-Order Functions for Parsing or Why Would Anyone Ever Want To Use a Sixth-Order Function?' by Chris Okasaki. It's written using ML, but the ideas apply equally to Haskell.

yatima2975
  • 6,580
  • 21
  • 42
9

Joel Spolsky wrote a famous essay demonstrating how Map-Reduce works using Javascript's higher order functions. A must-read for anyone asking this question.

bgibson
  • 17,379
  • 8
  • 29
  • 45
7

Higher-order functions are also required for currying, which Haskell uses everywhere. Essentially, a function taking two arguments is equivalent to a function taking one argument and returning another function taking one argument. When you see a type signature like this in Haskell:

f :: A -> B -> C

...the (->) can be read as right-associative, showing that this is in fact a higher-order function returning a function of type B -> C:

f :: A -> (B -> C)

A non-curried function of two arguments would instead have a type like this:

f' :: (A, B) -> C

So any time you use partial application in Haskell, you're working with higher-order functions.

C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
6

Martín Escardó provides an interesting example of a higher-order function:

equal :: ((Integer -> Bool) -> Int) -> ((Integer -> Bool) -> Int) -> Bool

Given two functionals f, g :: (Integer -> Bool) -> Int, then equal f g decides if f and g are (extensionally) equal or not, even though f and g don't have a finite domain. In fact, the codomain, Int, can be replaced by any type with a decidable equality.

The code Escardó gives is written in Haskell, but the same algorithm should work in any functional language.

You can use the same techniques that Escardó describes to compute definite integrals of any continuous function to arbitrary precision.

Russell O'Connor
  • 2,222
  • 1
  • 16
  • 16
4

One interesting and slightly crazy thing you can do is simulate an object-oriented system using a function and storing data in the function's scope (i.e. in a closure). It's higher-order in the sense that the object generator function is a function which returns the object (another function).

My Haskell is rather rusty so I can't easily give you a Haskell example, but here's a simplified Clojure example which hopefully conveys the concept:

(defn make-object [initial-value]
  (let [data (atom {:value initial-value})]
      (fn [op & args]
        (case op 
          :set (swap! data assoc :value (first args))
          :get (:value @data)))))

Usage:

(def a (make-object 10))

(a :get)
=> 10

(a :set 40)

(a :get)
=> 40

Same principle would work in Haskell (except that you'd probably need to change the set operation to return a new function since Haskell is purely functional)

mikera
  • 105,238
  • 25
  • 256
  • 415
  • I don't think you can say it *simulates* object-oriented programming. It depends on your definition of OOP: is inheritance (open recursion) a requirement or not? If it is, then what you shown is not an object, just a common form of state hiding. If it isn't, then what you shown *is* an object (and not a simulation thereof), only following an object protocol which is not standardized by the language. – gasche Apr 26 '11 at 17:12
  • @gasche - sure, this is only a simplified example. but if you wanted to add inheritance too it would be easy - e.g. you could do prototype-based inheritance with method implementations stored inside a hash table (which would also be a nice example of first-order functions stored in a data structure for the OP!) – mikera Apr 26 '11 at 17:23
  • Not really easy in Haskell, as it's very strict about type safety and has no intrinsic universal notion of subtyping. Using inheritance is awkward at best when the language provides no built-in means for, e.g., deciding whether one type can be (safely and automatically) substituted for another type. Otherwise, records of functions in Haskell make a very serviceable, if minimalist, prototype-based object system. This is of course no obstacle for languages with dynamic typing or built-in subtyping. – C. A. McCann Apr 26 '11 at 19:53
  • Haskell prefers to do inheritance-like things with typeclasses. See the various numeric type classes and instances, for example. – Phob May 13 '11 at 17:55
  • Please do not perpetuate that myth. Typeclasses are **not** for inheritance-like things, and are in fact wildly inappropriate for such a task. A simple functor is sufficient. – nomen Jun 07 '13 at 14:10
4

I'm a particular fan of higher-order memoization:

memo :: HasTrie t => (t -> a) -> (t -> a)

(Given any function, return a memoized version of that function. Limited by the fact that the arguments of the function must be able to be encoded into a trie.)

This is from http://hackage.haskell.org/package/MemoTrie

Darius Jahandarie
  • 760
  • 1
  • 5
  • 10
3

There are several examples here: http://www.haskell.org/haskellwiki/Higher_order_function

I would also recommend this book: http://www.cs.nott.ac.uk/~gmh/book.html which is a great introduction to all of Haskell and covers higher order functions.

Higher order functions often use an accumulator so can be used when forming a list of elements which conform to a given rule from a larger list.

Nick Brunt
  • 9,533
  • 10
  • 54
  • 83
3

Here's a small paraphrased code snippet:

rays :: ChessPieceType -> [[(Int, Int)]]
rays Bishop = do
  dx <- [1, -1]
  dy <- [1, -1]
  return $ iterate (addPos (dx, dy)) (dx, dy)
...  -- Other piece types

-- takeUntilIncluding is an inclusive version of takeUntil
takeUntilIncluding :: (a -> Bool) -> [a] -> [a]

possibleMoves board piece = do
  relRay <- rays (pieceType piece)
  let ray = map (addPos src) relRay
  takeUntilIncluding (not . isNothing . pieceAt board)
    (takeWhile notBlocked ray)
  where
    notBlocked pos =
      inBoard pos &&
      all isOtherSide (pieceAt board pos)
    isOtherSide = (/= pieceSide piece) . pieceSide

This uses several "higher order" functions:

iterate :: (a -> a) -> a -> [a]
takeUntilIncluding  -- not a standard function
takeWhile :: (a -> Bool) -> [a] -> [a]
all :: (a -> Bool) -> [a] -> Bool
map :: (a -> b) -> [a] -> [b]
(.) :: (b -> c) -> (a -> b) -> a -> c
(>>=) :: Monad m => m a -> (a -> m b) -> m b

(.) is the . operator, and (>>=) is the do-notation "line break operator".

When programming in Haskell you just use them. Where you don't have the higher order functions is when you realize just how incredibly useful they were.

yairchu
  • 23,680
  • 7
  • 69
  • 109
3

Here's a pattern that I haven't seen anyone else mention yet that really surprised me the first time I learned about it. Consider a statistics package where you have a list of samples as your input and you want to calculate a bunch of different statistics on them (there are also plenty of other ways to motivate this). The bottom line is that you have a list of functions that you want to run. How do you run them all?

statFuncs :: [ [Double] -> Double ]
statFuncs = [minimum, maximum, mean, median, mode, stddev]

runWith funcs samples = map ($samples) funcs

There's all kinds of higher order goodness going on here, some of which has been mentioned in other answers. But I want to point out the '$' function. When I first saw this use of '$', I was blown away. Before that I hadn't considered it to be very useful other than as a convenient replacement for parentheses...but this was almost magical...

mightybyte
  • 7,282
  • 3
  • 23
  • 39
  • `($) :: (a -> b) -> a -> b` It's just an operator for plain function application. "f $ a" is the same as "f a". It is right associative and has a precendence of 0. – mightybyte Apr 28 '11 at 00:46
  • For more detailed information check out this question http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign – mightybyte Apr 28 '11 at 16:00
2

One thing that's kind of fun, if not particularly practical, is Church Numerals. It's a way of representing integers using nothing but functions. Crazy, I know. <shamelessPlug>Here's an implementation in JavaScript that I made. It might be easier to understand than a Lisp/Haskell implementation. (But probably not, to be honest. JavaScript wasn't really meant for this kind of thing.)</shamelessPlug>

Tyler
  • 21,762
  • 11
  • 61
  • 90
2

It’s been mentioned that Javascript supports certain higher-order functions, including an essay from Joel Spolsky. Mark Jason Dominus wrote an entire book called Higher–Order Perl; the book’s source is available for free download in a variety of fine formats, include PDF.

Ever since at least Perl 3, Perl has supported functionality more reminiscent of Lisp than of C, but it wasn’t until Perl 5 that full support for closures and all that follows from that was available. And ne of the first Perl 6 implementations was written in Haskell, which has had a lot of influence on how that language’s design has progressed.

Examples of functional programming approaches in Perl show up in everyday programming, especially with map and grep:

@ARGV    = map { /\.gz$/ ? "gzip -dc < $_ |" : $_ } @ARGV;

@unempty = grep { defined && length } @many;

Since sort also admits a closure, the map/sort/map pattern is super common:

@txtfiles = map { $_->[1] }
            sort { 
                    $b->[0]  <=>     $a->[0]
                              ||
                 lc $a->[1]  cmp  lc $b->[1]
                              ||
                    $b->[1]  cmp     $a->[1]
            }
            map  { -s => $_ } 
            grep { -f && -T }
            glob("/etc/*");

or

@sorted_lines = map { $_->[0] }
                sort {
                     $a->[4] <=> $b->[4] 
                             ||
                    $a->[-1] cmp $b->[-1]
                             ||
                     $a->[3] <=> $b->[3]
                             ||
                     ...
                }
                map { [$_ => reverse split /:/] } @lines;

The reduce function makes list hackery easy without looping:

$sum = reduce { $a + $b } @numbers;

$max = reduce { $a > $b ? $a : $b } $MININT, @numbers;

There’s a lot more than this, but this is just a taste. Closures make it easy to create function generators, writing your own higher-order functions, not just using the builtins. In fact, one of the more common exception models,

try {
   something();
} catch {
   oh_drat();
};

is not a built-in. It is, however, almost trivially defined with try being a function that takes two arguments: a closure in the first arg and a function that takes a closure in the second one.

Perl 5 doesn’t have have currying built-in, although there is a module for that. Perl 6, though, has currying and first-class continuations built right into it, plus a lot more.

tchrist
  • 78,834
  • 30
  • 123
  • 180