Currying in TypeScript (just for fun)

Javier Toledo - Jun 23 '20 - - Dev Community

Let's have some fun with TypeScript syntax. Thanks to the fat arrow interface, you can easily match (with some caveats) one of the best features of Haskell called currying. Currying is the ability to partially apply a function. Let's build a small function that concats strings first, we'll see a situation on which this technique can be useful.

An obvious reference implementation for our cat function could be:

function cat(a: string, b: string): string {
  return a + ' ' + b
}
Enter fullscreen mode Exit fullscreen mode

But this function can't be applied partially if we call it with just the first parameter, we'll have a funny result (thanks JavaScript):

cat("hello")
=> "hello undefined"
Enter fullscreen mode Exit fullscreen mode

In Haskell all functions are curried by default, so the obvious implementation will behave very differently:

cat :: String -> String -> String
cat a b = a ++ " " ++ b

-- Calling it with one parameter returns a 
-- partially applied function
let catHello = cat "hello" 

-- we can apply the other parameter later
catHello "World" 
=> "Hello World" 
Enter fullscreen mode Exit fullscreen mode

The fat arrow syntax and TypeScript type annotations allow us to implement currying in a non really that surprisingly similar way. The trick is very easy, we just need to nest single-parameter functions that return a function for the next parameter, writing the implementation in the last function:

let cat: (a: string) => (b: string) => string =
    (a) => (b) => a + ' ' + b

// To apply the whole function, you'd need to pass 
// the parameters separately:
console.log(cat('Hello')('World'))
=> "Hello World"

let catHello = cat('Hello')

console.log(catHello('World'))
=> "Hello World"
Enter fullscreen mode Exit fullscreen mode

If you're wondering how this can possibly work: A function defined in another function can access the scope of the previous function via closures, so inner functions can see the parameter values of previous ones. You can learn more about this useful technique here.

Notice that if we could remove the parentheses, the implementation would be almost the same as Haskell's!

Why partial application is interesting?

It is a nice way to inject context or dependencies into functions without wrapping them in classes, and they're an awesome tool combined with iterators or to build callbacks:

let dbWrite: (dbConnection: DBConnection) => (data: string) => bool =
  (dbConnection) => (data) => ...

let productionDBWrite = dbWrite(productionDBConnection)

request.data.map(productionDBWrite)
Enter fullscreen mode Exit fullscreen mode

BUT... you probably want to use bind instead!

As I said at the beginning of the article, I wrote it just for fun, I love to find how languages like Haskell inspire features and new ways of thinking in other more trendy languages.

In JavaScript you can use Function.prototype.bind() to achieve similar results:

function cat(a: string, b: string): string {
  return a + ' ' + b
}

let catHello = cat.bind(null, "Hello") 
// Take into account that the first parameter defines 
// where "this" points to in the bound function. It's 
// important to remember this if you're building a 
// bound function from a class method. As we don't 
// reference `this` in our function, it's fine to just 
// pass `null` here.

console.log(catHello('World'))
=> "Hello World"

Enter fullscreen mode Exit fullscreen mode

We use this technique extensively in Booster Framework to reduce code complexity. Feel free to join our community and practice some hardcore TypeScript techniques there, contributions from any skill level are highly appreciated, there's nothing like learning together!

. . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player