How to do it
In Haskell, this is actually very simple:
Prelude Control.Monad> liftM2 (+) (* 2) (* 3) 10 50
Or alternatively:
Prelude Control.Applicative> (+) <$> (* 2) <*> (* 3) $ 10 50
Or, in more detail:
import Control.Monad main :: IO () main = do let (+$) = liftM2 (+) -- Define a new infix operator let double = (* 2) let triple = (* 3) print $ (double +$ triple) 10
What's going on here? Haskell is a very alien language if your experience is in PHP and JavaScript, so I will try to break it. But only heads-up: the corresponding functions here ( liftM2 and <$> / <*> ) are much more general than just this use, which can make it difficult to understand at first. The resulting force, however, is worth it.
High Level Summary
Here is my attempt at a very high level: liftM2 accepts the binary function op and creates a binary function that works with "more complex" values. If your "more complex" values ​​are functions with one argument f and g , there is only one good way to combine them and create a new function with one argument: create a function that passes its argument x to f and g , and then computes fx `op` gx -it, combines their results using op . In JavaScript, it will be something like
function liftM2_functions(op) { return function (f,g) { return function (x) { op(f(x), g(x)) } } }
Since liftM2 only works with binary functions, there is also liftM3 for ternary functions, etc., but it is dirty; thus, the operators <$> and <*> were introduced, such that liftM n f a1 a2 ... a n is exactly equivalent to f a1 <*> a2 <*> ... <*> a n .
More details
If you want a more detailed answer, read on. I will warn you that I am not sure how clear this is; he relies on some concepts that I have to worry about, and I may not have done a good job. But if you play, we go.
First, let's just explain the Haskell syntax:
(* 2) and (* 3) are short ways to write \x -> x*2 and \x -> x*3 , which are just Haskell for JavaScript function (x) { return x*2 } and function (x) { return x*3 } .(+) is just a function that performs the addition; this is the prefix form of the infix operator + .fx is a f(x) application application; fxy parsed as (fx) y , but can be considered as an application with two arguments f(x,y) .- The infix operator
$ also a functional application, but with a lower priority: ... $ x parsed as (...) x . <$> and <*> are fancy infix operators.
Types
So! From this point of view, we first consider the first fragment. In liftM2 (+) (* 2) (* 3) 10 "function of adding functions" you want liftM2 (+) ; its two arguments: (* 2) and (* 3) ; and the argument to the result is 10 . What's happening? In Haskell, the right way to think about it is in terms of types. Here are the relevant types, but be careful, you probably won't immediately understand them.
liftM2 :: Monad m => (a -> b -> r) -> ma -> mb -> mr (+) :: Num a => a -> a -> a (* 2) :: Num a => a -> a (* 3) :: Num a => a -> a 10 :: Num a => a
Here f :: t means that " f is of type t ". These types are pretty abstract, so some things are simplified. When you see Num a , it means "for some type a , which is a number"; we can think about it, for example, Int or Double . So we get
(+) :: Int -> Int -> Int (* 2) :: Int -> Int (* 3) :: Int -> Int 10 :: Int
Well, 10 is an integer. What about (* 2) and (* 3) ? These are functions, indicated by the -> symbol, which map an integer to an integer. You know that (+) is a binary function for integers, so you might think that it will be of type (Int,Int) -> Int . However, in Haskell, the right way to think about it is a function that takes an integer and returns another function; this is called currying. In JavaScript, this will be implemented as
function add(x) { return function (y) { return x + y } }
You may not understand why this is good, but it will become relevant.
Now let's look at liftM2 :: Monad m => (a -> b -> r) -> ma -> mb -> mr . What's going on here? Ignoring the Monad m bit, this suggests that liftM2 uses a function with two arguments, "monadic a " and "monadic b ", and produces "monadic r ". However, thanks to currying, this is the same as saying that liftM2 accepts a function with two arguments and returns a function with two arguments, the arguments and results of which are monodic: liftM2 :: Monad m => (a -> b -> r) -> (ma -> mb -> mr) . So what happens here: liftM2 (+) is the monadic version of the add.
Monads (don't panic!)
Now I continue to use the word monadic, and I have not defined it. And I'm not going to! There are many monad textbooks on the Internet, and some of them are even good; if you're interested, see "You could come up with monads!" , Here is all that you need to understand at the moment: the monadic meaning is that it is "supplemented" in some way, having some additional structure around it. Since lists are monads, [1,2,3] is the monadic int in the list monad; there, the structure is that it is an int, which can be one, two, or three. Here we are interested in the reader monad: the monadic int in this case is just a function that takes some object of type a and returns int. The idea is that it is an integer that can read and depend on some medium.
So what we have here is just that. (* 2) is a function that takes and returns an integer. Specializing in the type liftM2 for using the reader monad, we get the following:
liftM2 :: (a -> b -> r) -> ((e -> a) -> (e -> b) -> (e -> r))
Now this is promising! The liftM2 function takes a binary function and creates a function that itself acts on the function. Applying it to (+) :: Int -> Int -> Int , we get
liftM2 (+) :: (e -> Int) -> (e -> Int) -> (e -> Int)
If you think about it, this is the type of adding functions: liftM2 (+) takes two functions with one argument and creates a new function with one argument, and it does this by adding its own results.
Now, how is liftM2 implemented? Thus (the reformatted version is the real implementation ):
liftM2 f m1 m2 = do x1 <- m1 x2 <- m2 return $ f x1 x2
What's going on here? This says: "Get a value of type a from m1 , get another one from m2 and combine these values ​​with the given f ." Inside our reader monad (functions with one argument), m1 :: e -> Int and x1 <- m1 apply m1 to the "environment", which in our example is 10 . So, in our case, we have
do doubled <- (* 2) tripled <- (* 3) return $ doubled + tripled
All this expression, of course, should depend on the secret environment, since I never mentioned it anywhere. So its type is Int -> Int : a function that takes an int, doubles and triples it, and advertises the two together. This, of course, is where we started.
Applicative functors (don't panic!)
What about <$> and <*> ? I explained them at a very high level above, and I can tell you the main idea: <$> is similar to the liftM1 function, and <*> applies "complex" functions to "complex" values. Their actual types
(<$>) :: Functor f => (a -> b) -> fa -> fb (<*>) :: Applicative f => f (a -> b) -> fa -> fb
I won’t explain it in detail here because I don’t think I can do a very good job. In one sentence, an object in a functor is, in a sense, a simple value with additional complications, and <$> (also called fmap ) leaves only complications and applies the function to the plain old value inside. And <*> is part of an additional structure that allows you to apply functions to several simple values ​​inside several structures at the same time. But if you need more information, it's there (although I don't know a good resource, people often recommend Learn You A Haskell ).
Conclusion
Using some powerful constructions (applicative functors and / or monads), we can get this concept of adding a function mostly for free, and much more. (Multiplication function? liftM2 (*) . Function level if-then-else? liftM3 if' where if' ctf = if c then t else f etc.) And the great thing about Haskell is that these advanced functions baked. Functors and monads are defined in Prelude, a module that is implicitly included in every Haskell program (for example, the Java namespace java.lang ); the Control.Monad module (which defines liftM2 ) and the Control.Applicative module (which defines the applicative functors, <$> and <*> ) are part of the standard library 1 . Haskell's higher order and statically typed character allows these concepts to be used; doing this in PHP or Java would be basically impossible, and although JavaScript could express some of the concepts, the fact that it dynamically typed means that the implementation should look very different. Other languages, such as Scala, can and can use these concepts, although Scala is the only thing I can remember from the top of my head (and they are presented in Scalaz , not the standard library). The message that access to these powerful abstractions allows you to write simpler code and less code, do less abstract things, such as adding functions that you want to do.
1 In fairness, Control.Applicative not part of the Haskell 2010 standard. However, it is enabled by default with GHC, by far the most used Haskell compiler (it is the de facto standard), as well as Hugs (the Haskell interpreter, which had significant use but lost ground for GHC) and, as far as I can tell, JHC and YHC (two more compilers). And there is nothing stopping the unrelated Haskell compiler from creating Control.Applicative, since the code is fully compliant with Haskell 2010.