DEV Community

Khoa Pham
Khoa Pham

Posted on

Curry in Swift and Javascript

You may encounter curry in everyday code without knowing it. Here is a bit of my reflections on curry and how to apply it in Javascript and Swift.

Taking one parameter in Haskell

In Haskell, all function officially takes only 1 parameter. Function with many parameters are just curried, which means they will be partially applied. Calling sum 1 just returns a function with 1 parameter, then 2is passed into this function. The following 2 function calls are the same.

ghci> sum 1 2
3 
ghci> (max 1) 2
3

I tend to think of curried function or partially applied function as something that carry dependencies at each application step. Each curried function can be assigned to variable or pass around, or as returned value.

Curry in Swift for predicate

When I was trying to make my own Signal library, I have

and Event

filter

Then there should be a filter for Signal. The idea of filter is that we should update signal if the Event is Next with right filtered value

public func filter(f: T -> Bool) -> Signal<T>{
    let signal = Signal<T>()
    subscribe { result in
        switch(result) {
        case let .Success(value):
            if f(value) {
                signal.update(result)
            }
        case let .Error(error): signal.update(.Error(error))
        }
    }
    return signal
}

2 parameters

But having Event as another monad, I think it should be more encapsulated if that switching logic gets moved into the Event. Here the filter takes 2 params

Event.swift

func filter(f: T -> Bool, callback: (Event<T> -> Void)) {
        switch self {
        case let .Next(value) where f(value):
            callback(self)
        case .Failed:
            callback(self)
        default:
            break
    }
}

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
    let signal = Signal<T>()

    subscribe { event in
        event.filter(f, callback: signal.update)
    }

    return signal
}

Currying

With currying, we can make filter a more abstract function, and defer the decision to pass the callback param. It is a little carried away but I find it helpful this way

Now filter accepts 1 param, and it returns a function that takes callback as its param

Event.swift

func filter(f: T -> Bool) -> ((Event<T> -> Void) -> Void) {
        return { g in
            switch self {
            case let .Next(value) where f(value):
                g(self)
            case .Failed:
                g(self)
            default:
                break
            }
        }
    }

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
        let signal = Signal<T>()

        subscribe { event in
            event.filter(f)(signal.update)
        }

        return signal
    }

Curry syntax in Swift 2 and above

Swift 2 supports curry syntax function

func sum(a: Int)(b: Int) -> Int {
    return a + b
}

let sumWith5 = sum(5)
let result = sumWith5(b: 10)

Unfortunately, the syntactic sugar for declaring curry has been dropped since Swift 3. You may want to find out in Bidding farewell to currying. But it’s not a big deal as we can easily create curry function. It is just a function that returns another function.

Using curry for partial application in UIKit

I used this curry technique in my Xkcd app. See MainController.swift. MainController is vanilla UITabBarController with ComicsController and FavoriteController , all embedded in UINavigationViewController .

The feature is that when a comic is selected, a comic detail screen should be pushed on top of the navigation stack. For example in ComicsController

/// Called when a comic is selected  
var selectComic: ((Comic) -> Void)?

All ComicsController needs to know is to call that selectComic closure with the chosen Comic, and someone should know how to handle that selection. Back to the handleFlow function inside MainController.

private func handleFlow() {
  typealias Function = (UINavigationController) -> (Comic) -> Void
  let selectComic: Function = { [weak self] navigationController in
    return { (comic: Comic) in
      guard let self = self else {
        return
      }

  let detailController = self.makeDetail(comic: comic)
      navigationController.pushViewController(detailController, animated: true)
    }
  }

  comicsController.selectComic = selectComic(comicNavigationController)
  favoriteController.selectComic = selectComic(favoriteNavigationController)
}

I declared Function as typealias to explicitly state the curry function that we are going to build

typealias Function = (UINavigationController) -> (Comic) -> Void

We build selectComic as curried function, that takes UINavigationViewController and returns a function that takes Comic and returns Void . This way when we partially apply selectComic with the a navigationController , we get another function that has navigationController as dependency, and ready to be assigned to selectComic property in comicsController .

Curry promised function in Javascript

I like to work with Promise and async/await in Javascript. It allows chainable style and easy to reason about. So when working with callbacks in Javascript, for example callback from native modules in React Native, I tend to convert them into Promise.

For example when working with HealthKit, we need to expose a native modules around it

// [@flow](http://twitter.com/flow)

import { NativeModules } from 'react-native'

type HealthManagerType = {
  checkAuthorisation: ((string) => void)) => void,
  authorise: ((boolean) => void)) => void,
  readWorkout: (Date, Date, () => void)) => void,
  readDailySummary: (Date, Date, () => void)) => void,
  readMeasurement: (Date, Date, () => void)) => void
}

const HealthManager: HealthManagerType = NativeModules.HealthManager
export default HealthManager

We can build a toPromise function that can convert a function with callback into Promise

// [@flow](http://twitter.com/flow)

const toPromise = (f: (any) => void) => {
  return new Promise<any>((resolve, reject) => {
    try {
      f((result) => {
        resolve(result)
      })
    } catch (e) {
      reject(e)
    }
  })
}

export default toPromise

However, as you can see in the signature, it only works with a callback of type (any) => void In other words, this callback must have exactly 1 parameter, because a Promise can either returns a value or throws an error.

To remedy this, we can build a curry function that can turns function with either 1, 2, 3 parameters into curried function. Thanks to the dynamic nature of Javascript, we have

// [@flow](http://twitter.com/flow)

function curry0(f: () => void) {
  return f()
}

function curry1(f: (any) => void) {
  return (p1: any) => {
    return f(p1)
  }
}

function curry2(f: (any, any) => void) {
  return (p1: any) => {
    return (p2: any) => {
      return f(p1, p2)
    }
  }
}

function curry3(f: (any, any, any) => void) {
  return (p1: any) => {
    return (p2: any) => {
      return (p3: any) => {
        return f(p1, p2, p3)
      }
    }
  }
}

export default {
  curry0,
  curry1,
  curry2,
  curry3
}

So with a function that have 3 parameters, we can use curry3 to partially apply the first 2 parameters. Then we have a function that accepts just a callback, and this is turned into Promise via toPromise

const readWorkout = curry.curry3(HealthManager.readWorkout)(DateUtils.startDate))(DateUtils.endDate))

const workouts = await toPromise(readWorkout)




Where to go from here

Here are some of my favorite posts to read more about curry

Original post https://medium.com/fantageek/curry-in-swift-and-javascript-bcd1245b30d3

Top comments (0)