Currying in TypeScript (just for fun)
Tags: currying, haskell, typescript
Published: 2020-06-23 10:30:35

---
title: Currying in TypeScript (just for fun)
published: true
description: You can easily match Haskell's best feature in TypeScript: Currying. The ability to partially applying a function with a non-terrible implementation.
tags: currying, haskell, typescript
---

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](https://wiki.haskell.org/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:

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

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):

```typescript
cat("hello")
=> "hello undefined"
```

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

```haskell
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" 
```

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:

```typescript 
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"
```

> 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures).

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:

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

let productionDBWrite = dbWrite(productionDBConnection)

request.data.map(productionDBWrite)
```

# 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()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) to achieve similar results:

```typescript
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"

```

We use this technique extensively in [Booster Framework](https://booster.cloud) to reduce code complexity. Feel free to [join our community](https://spectrum.chat/boostercloud?tab=posts) and practice some hardcore TypeScript techniques there, contributions from any skill level are highly appreciated, there's nothing like learning together!