How to curry a higher-order function without obscuring type?

Given a standard-looking (I think?) curry function:

func curry<T, U, Z>(_ ƒ: @escaping (T, U) -> Z) -> (T) -> (U) -> Z {
  return { t in
    return { u in
      ƒ(t, u)
    }
  }
}

and a higher-order-looking function like:

func higherOrder(_ x: String, _ y: @escaping (String) -> Int) { }

currying off the first param makes the type of the resulting function unknowable:

type(of: curry(higherOrder)("foo"))
//> ((String) -> Int) -> ()
curry(higherOrder)("foo") is ((String) -> Int) -> ()
//> false?!?!

First, am I doing this right, or is my syntax for expressing types off?

If it looks correct, is this a known thing? Are there workarounds?

1 Like

This might be a more general issue related to stuffing a whole function into a single generic placeholder and then stuffing that into the param of another function:

func putInParam<T>(_ x: T) -> (T)->Void {
  return { (_: T)->Void in }
}

type(of: putInParam({ (_: String)->Void in }))
//> ((String) -> ()) -> ()
putInParam({ (_: String)->Void in }) is ((String) -> ()) -> ()
//> false

Giving it a non-function works fine:

putInParam("foo") is (String) -> ()
//> true

Or putting it into the return instead of the params also works:

func putInReturn<T>(_ x: T) -> ()->T {
  return { return x }
}

putInReturn({ (_: String)->Void in }) is () -> (String) -> ()
//> true

It feels like I'm missing some subtlety of the type syntax. Like by wrapping a function in parens to indicate it's the parameter of another function, I'm accidentally asking swift to treat it like a tuple or something.

The type(of:) function is not actually showing the whole type when you print it. you need to include the @escaping in your is clause.

e.g.:

curry(higherOrder)("foo") is (@escaping (String) -> Int) -> ()

1 Like

@jemmons You can check "A problem" section in the episode about Higher-Order Functions on pointfree.co . I hope it helps!

In short: you have to use a little helper function (with a cool name zurry) that lowers a function that takes no arguments to just the value that it returns.