Foldl and folr are 2 very important functions for FP and Haskell, but I have never heard much about the unsided fold:
fold f [a,b,c,d] = (f (f a b) (f c d))
That is, a fold that operates on binary associative functions (so the order of application doesn't matter). If I recall correctly, this is very common in databases as it can be parallelized. So, about it, I ask:
Is it, like foldr, universal?
Like foldr, can you define every important function using it?
Is there a fusion rule for it, similar to those for foldr/build and unfoldr/destroy?
Why is it barely mentioned?
Any consideration worth mentioning?
This is often thought of as tree reduction and is important in parallel computation since it embodies divide and conquer reduction.
First, if the combining function is non-associative then obviously there are big differences between foldl, foldr, and "unsided fold", so let's assume that we combine with an associative operation. Immediately, all folds can be represented by a Monoid.
foldlm :: Monoid m => [m] -> m
foldlm = foldl mappend mempty
foldrm :: Monoid m => [m] -> m
foldrm = foldr mappend mempty
usfoldm :: Monoid m => [m] -> m
usfoldm = foldTree mappend mempty . buildTree
Which is better represented by foldMap :: Monoid m => (a -> m) -> [a] -> m which is defined by default using foldr.
foldMap f = foldr (mappend . f) mempty
Which is sufficient, if given a final extraction step, to produce tree-like unsided folding given a Monoid defined on a tree-like sequence type that controls how the element-Monoids are combined.
data Tree a
singleton :: a -> Tree a
instance Monoid (Tree a) where ...
foldTree :: Monoid a => Tree a -> a
foldTree . foldMap singleton :: Monoid a => [a] -> a
Finally, we've seen that we can get foldMap from foldr, but we can also get foldr from foldMap
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend (Endo f) (Endo g) = Endo (f . g)
foldr f z as = appEndo (foldMap (Endo . f) as) z
Generally, foldMap is considered to be more primitive since it lets the underlying Monoid choose its preferred folding method. This means that we're free to write more efficient or more parallel folds on a per-datatype level, though it can still be challenging to do so properly.
It's worth noting that the foldMap abstraction is usually found as a instance method of Foldable which is a very popular, but more new Haskell typeclass. It's also considered to be a little bit silly despite its practical usefulness because Foldable has very few meaningful laws excepting that
toList :: Foldable f => f a -> [a]
exists, which also lets us see the Monoidal nature of foldMap as [a] is the universal Monoid which we can recover with foldr.
For further investigation of fusion rules, it would be valuable to read about a proposed dual typeclass Buildable as in Gershom Bazerman's Building up to a Point via Adjunctions.
And finally, as for popularity, I think it's definitely the preferred method of instantiating Foldable these days since it allows for more efficient Monoid folds if necessary, but it's definitely newer than both foldl and foldr which likely plays into its relative obscurity.
The function you're looking for is Data.Foldable.foldMap.
foldMap :: Data.Monoid.Monoid m => (a -> m) -> t a -> m
This function is isomorphic to foldr. As a proof, note that one of foldMap or foldr is a minimal complete definition of Foldable, meaning that each can be written in terms of the other. That answers your first two questions affirmatively.
I don't know of fusion rules for foldMap specifically, but I'm sure that one could exist. At the very least, foldr fusion rules should apply to some extent.
I have no idea why it's barely mentioned.
One consideration worth mentioning is that, as far as lists are concerned, you can't always take full advantage of this fold. Since lists are constructed out of cons cells, doing a tree fold would mean traversing half the list, then recursing down each half and traversing half again, etc. This is a lot of extra traversals compared to foldl or foldr. For non-list structures a tree fold can be much more efficient, and even for lists it's possible to take some advantage of this. There was a nice blog about one such task recently.
Related
Well, suppose that I have a set of functional definitions (with a syntax tree) in church encoding :
true : λx -> λy -> x
false : λx -> λy -> y
Giving the definition λx -> λy -> y, it is very clear how to return the named definition, applying a matching with alpha-equivalence will be enough.
α true λx -> λy -> y = false
α false λx -> λy -> y = true
But consider the example below:
0 : λf λz -> x
succ : λn λf λx -> f (n f x)
3 : succ (succ (succ 0)))
So, when 3 suffers from beta-reduction it will unfold to some definition like :
3_unfolded : (λf -> (λx -> (f (f (f x))))) : (A -> A) -> A -> A
You can see the term can get bigger easily, of course, it is not a good way to represent pure data, because of the size of the term. So, I want to know if there is an algorithm able efficiently to rename again every definition after suffering evaluation. Them 3_unfolded will become (succ (succ (succ 0))) again by giving the set of definitions of natural church encoding (0, and succ only).
I know there are some side effects, like ambiguous representations, but let's ignore that (if you expand the same definition of succ and rename to succ_2, for example).
This is essentially the problem of beta-equivalence, and it’s undecidable in general; it also won’t necessarily produce usable output even when it could produce something, e.g. with some restrictions including strong normalisation. Therefore I think your best strategies here will be heuristic, because by default, reductions destroy information. The solutions are to retain information that you care about, or avoid needing information that’s gone. For instance:
Decouple the memory representation of terms from their LC representations, in particular cases where you care about efficiency and usability. For example, you can store and print a Church numeral as a Natural, while still allowing it to be converted to a function as needed. I think this is the most practical technical angle.
Retain information about the provenance of each term, and use that as a hint to reconstruct named terms. For example, if you know that a term arose by a given shape of beta-reduction, you can beta-expand/alpha-match to potentially rediscover an application of a function like succ. This may help in simple cases but I expect it will fall down in nontrivial programs.
Instead of considering this an algorithmic problem, consider it a usability design problem, and focus on methods of identifying useful information and presenting it clearly. For example, search for the largest matching function body that is also the most specific, e.g. a term might match both λx. x (identity) and λf. λx. f x (function application), but the latter is more specific, and even more specifically it can be a numeral (λs. λz. s z = 1); if there are multiple possibilities, present the most likely few.
Whenever you encounter a problem that’s undecidable for arbitrary programs, it’s worth remembering that humans write extremely non-arbitrary programs. So heuristic solutions can work remarkably well in practice.
Say I want to implement the Fermi function (the simplest example of a logistic curve) so that if it's passed a Float it returns a Float and if it's passed a Double it returns a Double. Here's what I've got:
e = 2.7182845904523536
fermiFunc :: (Floating a) => a -> a
fermiFunc x = let one = fromIntegral 1 in one/(one + e^(-x))
The problem is that ghc says e is a Double. Defining the variable one is also kinda gross. The other solution I've thought of is to just define the function for doubles:
e = 2.7182845904523536
fermiFuncDouble :: Double -> Double
fermiFuncDouble x = 1.0/(1.0 + e^(-x))
Then using Either:
fermiFunc :: (Floating a) => Either Float Double -> a
fermiFunc Right x = double2Float (fermiFuncDouble (float2Double x))
fermiFunc Left x = fermiFuncDouble x
This isn't very exciting though because I might as well have just written a separate function for the Float case that just handles the casting and calls fermiFuncDouble. Is there a nice way to write a function for both types?
Don't write e^x, ever, in any language. That is not the exponential function, it's the power function.
The exponential function is called exp, and its definition actually has little to do with the power operation – it's defined, depending on your taste, as a Taylor series or as the unique solution to the ordinary differential equation d⁄d𝑥 exp 𝑥 = exp 𝑥 with boundary condition exp 0 = 1. Now, it so happens that, for any rational n, we have exp n ≡ (exp 1)n and that motivates also defining the power operation for numbers in ℝ or ℂ addition to ℚ, namely as
az := exp (z · ln a)
...but e𝑥 should be understood as really just a shortcut for writing exp(𝑥) itself.
So rather than defining e somewhere and trying to take some power of it, you should use exp just as it is.
fermiFunc :: Floating a => a -> a
fermiFunc x = 1/(1 + exp (-x))
...or indeed
fermiFunc = recip . succ . exp . negate
Assuming that you want floating point exponent, that's (**). (^) is integral exponent. Rewriting your function to use (**) and letting GHC infer the type gives:
fermiFunc x = 1/(1 + e ** (-x))
and
> :t fermiFunc
fermiFunc :: (Floating a) => a -> a
Since Float and Double both have Floating instances, fermiFunc is now sufficiently polymorphic to work with both.
(Note: you may need to declare a polymorphic type for e to get around the monomorphism restriction, i.e., e :: Floating a => a.)
In general, the answer to "How do I write a function that works with multiple types?" is either "Write it so that it works universally for all types." (parametric polymorphism, like map), "Find (or create) one or more typeclasses that they share that provides the behaviour you need." (ad hoc polymorphism, like show), or "Create a new type that is the sum of those types." (like Either).
The latter two have some tradeoffs. For instance, type classes are open (you can add more at any time) while sum types are closed (you must modify the definition to add more types). Sum types require you to know which type you are dealing with (because it must be matched up with a constructor) while type classes let you write polymorphic functions.
You can use :i in GHCi to list instances and to list instance methods, which might help you to locate a suitable typeclass.
In Haskell, when writing a function, it means we map something(input) to another thing(output). I tried LYAH to understand the definition of Functor: seems just the same like a normal Functor.
Is there any restriction that a function could be called a Functor?
Is Functor allowed to have I/O or any other side effect?
If in Haskell, "everthing is a function", then what's the point of introducing the "Functor" concept? A restricted version of function, or an enhancement version of a function?
Very confused, need your advice.
Thanks.
First of all, it's not true that "everything is a function" in Haskell. Many things are not functions, like 4. Or the string "vik santata".
In Haskell, a function is something which maps some input to an output. A function is a value which you can apply to some other value to get a result. If a value has a -> in its type, chances are that it may be a function (but there are infinitely many exceptions to this rule of thumb ;-)).
Here are some examples of functions (quoting from a GHCI session):
λ: :t fst
fst :: (a, b) -> a
λ: :t even
even :: Integral a => a -> Bool
Here are a few examples of things which are not functions:
A (polymorphic) value which can assume any type a provided that the type is a member of the Num class (e.g. Int would be a valid type). The exact value would be inferred from how the number is used.
Note that this type has => in it, which is something different altogether than ->. It denotes a "class constraint".
λ: :t 5
5 :: Num a => a
A list of functions. Note that this has a -> in its type, but it's not the top level type constructor (the toplevel type is [], i.e. "list"):
λ: :t [fst, snd]
[fst, snd] :: [(a, a) -> a]
Functors are not things you can apply to values. Functors are types whose values can be used with (and returned by) the fmap function (provided that the fmap function complies to certain rules, often called 'laws'). You can find a basic list of types which are part of the Functor using GHCI:
λ: :i Functor
[...]
instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
[...]
This means you can apply fmap to lists, or to Maybe values, or to Either values.
It helps to know a little category theory. A category is just a set of objects with arrows between them. They can model many things in mathematics, but for our purposes we are interested in the category of type; Hask is the category of Haskell types, with each type being an object in Hask and each function being an arrow between the argument type and the return type. For example, Int, Char, [Char], and Bool are all objects in Hask, and ord :: Char -> Int, odd :: Int -> Bool, and repeat :: Char -> [Char] would be some examples of arrows in Hask.
Each category has several properties:
Every object has an identity arrow.
Arrows compose, so that if a -> b and b -> c are arrows, then so is a -> c.
Identity arrows are both left and right identities for composition.
Composition is associative.
The reason that Hask is a category is that every type has an identity function, and functions compose. That is, id :: Int -> Int and id :: Char -> Char are identity arrows for the category, and odd . ord :: Char -> Bool are composed arrows.
(Ignore for now that we think of id is polymorphic function with type a -> a instead of a bunch of separate functions with concrete types. This demonstrates a concept in category theory called a natural transformation that you don't need to think about now.)
In category theory, a functor F is a mapping between two categories; it maps each object of one category to an object of the other, and it also maps each arrow of one category to an arrow of the other. If a is an object in one category, we say that F a is the object in the other category. We also say that if f is an arrow in the first category, the corresponding arrow in the other if F f.
Not just any mapping is a functor. It has to obey two properties which should look familiar.
F has to map the identity arrow for an object a to the identity arrow of the object F a.
F has to preserve composition. That means that the composition of two arrows in the first category has to be mapped to the composition of the corresponding arrows in the other category. That is, if h = g ∘ f is in the first category, then h is mapped to F h = F g ∘ F f in the other.
Finally, an endofunctor is a special name for a functor that maps one category to itself. In Hask, the typeclass Functor captures the idea of an endofunctor from Hask to Hask. The type constructor itself maps the types, and fmap is used to map the arrows.
Let's take Maybe as an example. The type constructor Maybe is an endofuntor, because it maps objects in Hask (types) to other objects in Hask (other types). (This point is obscured a little bit since we don't have new names for the target types, so think of Maybe as mapping Int to the type Maybe Int.)
To map an arrow a -> b to Maybe a -> Maybe b, we provide a defintion for fmap in the instance of Maybe Int.
Maybe also maps functions, but using the name fmap instead. The functor laws it must obey are the same as two listed in the definition of a functor.
fmap id = id (Maps id :: Int -> Int to id :: Maybe Int -> Maybe Int.
fmap f . fmap g = fmap f . g (That is, fmap odd . fmap ord $ x has to return the same value as fmap (odd . ord) $ x for any possible value x of type Maybe Int.
As an unrelated tangent, others have pointed out that some things in Haskell are not functions, namely literal values like 4 and "hello". While true in the programming language (you can't, for instance, compose 4 with another function that takes an Int as a value), it is true that in category theory that you can replace values with functions from the unit type () to the type of the value. That is, the literal value 4 can be thought of as an arrow 4 :: () -> Int that, when applied to the (only) value of type (), it returns a value of type Int corresponding to the integer 4. This arrow would compose like any other; odd . 4 :: () -> Bool would map the value from the unit type to a Boolean value indicating whether the integer 4 is odd or not.
Mathematically, this is nice. We don't have to define any structure for types; they just are, and since we already have the idea of a type defined, we don't need a separate definition for what a value of a type is; we just just define them in terms of functions. (You might notice we still need an actual value from the unit type, though. There might be a way of avoiding that in our definition, but I don't know category theory well enough to explain that one way or the other.)
For the actual implementation of our programming language, think of literal values as being an optimization to avoid the conceptual and performance overhead of having to use 4 () in place of 4 every time we just want a constant value.
Actually, a functor is two functions, but only one of them is a Haskell function (and I'm not sure it's the function you suspect it to be).
A type level function. The objects of the Hask category are types with kind *, and a functor maps such types to other types. You can see this aspect of functors in ghci, using the :kind query:
Prelude> :k Maybe
Maybe :: * -> *
Prelude> :k []
[] :: * -> *
Prelude> :k IO
IO :: * -> *
What these functions do is rather boring: they map, for instance,
Int to Maybe Int
() to IO ()
String to [[Char]].
By this I don't mean that they map integer numbers to maybe-integers etc. – that's a more specific operation, not possible for every functor. I just mean, they map the type Int, as a single entity, to the type Maybe Int.
A value-level function, which maps morphisms (i.e. Haskell functions) to morphisms. The target morphism always maps between the types that result from applying the type-level function to the domain and codomain of the original function.This function is what you get with fmap:
fmap :: (Int -> Double) -> (Maybe Int -> Maybe Double)
fmap :: (() -> Bool) -> (IO () -> IO Bool)
fmap :: (Char -> String) -> String -> [String].
For something to be a functor - you need two things:
a container type*
a special function that converts a function from containees to a function converting containers
the first is depending on your own definition but the second one has been codified in the "interface" called Functor and the conversion function has been named fmap.
thus you always start with something like
data Maybe a = Just a | Nothing
instance Functor Maybe where
-- fmap :: (a -> b) -> Maybe a -> Maybe b
fmap f (Just a) = Just (f a)
fmap _ Nothing = Nothing
Functions on the other hand - do not need a container to work - so they are not related to Functor in that kind of way. On the other hand every Functor, has to implement the function fmap to earn its name.
Moreover convention wants a Functor to adhere to certain laws - but this cannot be enforced by the compiler/type checker.
*: this container can also be a phantom type, e.g. data Proxy a = Proxy in this case the name container is debatable, but I would still use that name
Not everything in Haskell is a function. Non-functions include "Hello World", (3 :: Int, 'a'), and Just 'x'. Things whose types include => are not necessarily functions either, although GHC (generally) translates them into functions in its intermediate representation.
What is a functor? Given categories C and D, a functor f from C to D consists of a mapping fo from the objects of C into the objects of D and a mapping fm from the morphisms of C into the morphisms of D such that
If x and y are objects in C and p is a morphism from x to y then fm(p) is a morphism from fo(x) to fo(y).
If x is an object in C and id is the identity morphism from x to x then fm(id) is the identity morphism from fo(x) to fo(x).
If x, y, and z are objects in C, p is a morphism from y to z, and q is a morphism from x to y, then fm(p . q) = fm(p).fm(q), where the dot represents morphism composition.
How does this relate to Haskell? We like to think of Haskell types and Haskell functions between them as forming a category. This is only approximately true, for various reasons, but it's a useful guide to intuition. The Functor class then represents injective endofunctors from this Hask category to itself. In particular, a Functor consists of a mapping (specifically a type constructor or partial application of type constructor) from types to types, along with a mapping (the fmap function) from functions to functions which obeys the above laws.
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 :)
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.
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.
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.
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.
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.
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.
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.
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)
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
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.
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.
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...
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>
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.
From section 3.13.3 of the curry tutorial:
Operations that residuate are called rigid , whereas operations that narrow are called flexible. All defined operations are flexible whereas most primitive operations, like arithmetic operations, are rigid since guessing is not a reasonable option for them. For example, the prelude defines a list concatenation operation as follows:
infixr 5 ++
...
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : xs ++ ys
Since the operation “++” is flexible, we can use it to search for a list satisfying a particular property:
Prelude> x ++ [3,4] =:= [1,2,3,4] where x free
Free variables in goal: x
Result: success
Bindings:
x=[1,2] ?
On the other hand, predefined arithmetic operations like the addition “+” are rigid. Thus, a
call to “+” with a logic variable as an argument flounders:
Prelude> x + 2 =:= 4 where x free
Free variables in goal: x
*** Goal suspended!
Curry does not appear to guard against writing goals that will be suspended. What type systems can detect ahead of time whether a goal is going to be suspended?
What you've described sounds like mode checking, which generally checks what outputs will be available for a certain set of inputs. You may want to check the language Mercury which takes mode checking quite seriously.