A blog exploring functional programming and Swift.

Wednesday Aug 15, 2018

Last week we concluded our 3-part introductory series to the `zip`

function. In the
second episode of that series we saw that many types support
`zip`

-like operations, even though we don’t typically think of those types in that way. For example,
two `Result`

values can be zipped up, and even two functions with the same input can be zipped. At the end of
the episode we provided some exercises to help viewers dive a little deeper into what `zip`

had to offer, and
this week we solve most of those problems!

Can you make the

`zip2`

function on our`Parallel`

type thread safe?

You can use GCD’s `DispatchGroup`

to
precisely coordinate two units of work so that we are notified when they are both finished, and we avoid
the racing problems we mentioned in the episode. Here’s how we can use it:

```
func zip2<A, B>(_ fa: Parallel<A>, _ fb: Parallel<B>) -> Parallel<(A, B)> {
return .init { callback in
let group = DispatchGroup()
var a: A!
var b: B!
group.enter()
fa.run { a = $0; group.leave() }
group.enter()
fb.run { b = $0; group.leave() }
group.notify(queue: .main) {
callback((a, b))
}
}
}
```

Here we have chosen to notify the group’s completion on the main thread, but a more robust `Parallel`

implementation might make that customizable. It’s interesting to think of `DispatchGroup`

‘s as just
GCD’s version of a `zip`

operation. We feel that `zip`

is a little more expressive and composable
than `DispatchGroup`

’s are.

Generalize the

`Parallel`

type to a type that allows returning values other than`Void`

:`struct F4<A, R> { let run: (@escaping (A) -> R) -> R }`

. Define`zip2`

and`zip2(with:)`

on the`A`

type parameter.

Although this `F4`

type is closely related to `Parallel`

, its `zip`

implementation is a bit different.
If we try to repeat what we did for `Parallel`

above we will quickly run into the problem that we must
return an `R`

value from each of the `fa.run`

and `fb.run`

functions, and we don’t have any such value.
Instead, we can take the approach we first took in episode two
when trying to define `zip`

on `Parallel`

, and we will nest the `run`

blocks:

```
func zip2<A, B, R>(_ fa: F4<A, R>, _ fb: F4<B, R>) -> F4<(A, B), R> {
return .init { callback in
fa.run { a in
fb.run { b in
callback((a, b))
}
}
}
}
```

Find a function in the Swift standard library that resembles the function above. How could you use

`zip2`

on it?

Have you ever looked at the method signature of `withUnsafeBytes`

? I mean, *really* looked at it? Here it
is:

`func withUnsafeBytes<Result>(_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result`

There’s a lot of noise in this function signature, so let’s clear it up a bit by removing all the `throws`

stuff and shortening the `Result`

generic name:

`func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) -> R) -> R`

Ok interesting. If we focus on just the shape of this, we see it’s a function of the form:
`((UnsafeRawBufferPointer) -> R) -> R`

. That is basically `F4<UnsafeRawBufferPointer, R>`

as defined
in exercise 2!

Unfortunately, we cannot just wrap these functions up in an `F4`

value, and then start zipping them. The
`throws`

and `rethrows`

annotations make these function signatures distinct from that of `F4`

. Instead
of littering our `F4`

type with `throws`

annotations, let’s just define a specialized `zip2`

for functions
of the form `((A) throws R) throws R`

:

```
func zip2<A, B, R>(
_ f: @escaping ((A) throws -> R) throws -> R,
_ g: @escaping ((B) throws -> R) throws -> R
) -> (@escaping (A, B) throws -> R) throws -> R {
return { callback in
try f { a in
try g { b in
try callback(a, b)
}
}
}
}
```

Ok, now that we have `zip`

defined, how can we use it? Well, imagine we had some C function that was imported
that operates on `UnsafeRawBufferPointer`

values. Then we could `zip`

up the `withUnsafeBytes`

of two
arrays and invoke that C function:

```
var xs = [1, 2, 3]
var ys = [4, 5, 6]
func someCFunction(_ x: UnsafeRawBufferPointer, _ y: UnsafeRawBufferPointer) -> Int {
// Do something with the pointers here...
return 1
}
try (zip2(xs.withUnsafeBytes, ys.withUnsafeBytes)) { x, y in
someCFunction(x, y)
}
```

This allows you to clearly express that you want to grab the underlying bytes of the array storage
and invoke a C function with those contents. The alternative way is to nest multiple calls to
`withUnsafeBytes`

, which leads to highly indented code and “callback hell”:

```
try xs.withUnsafeBytes { x in
try ys.withUnsafeBytes { y in
someCFunction(x, y)
}
}
```

Notice that we had to nest two layers deep, and if we were to need more unsafe bytes the indentation would
continue to grow. The `zip`

method for handling unsafe bytes is shorter and more expressive.

This exercise consists of multiple parts, and aims to explore what happens when you nest two types that each support a

`zip`

operation.

Consider the type

`[A]? = Optional<Array<A>>`

. The outer layer`Optional`

has`zip2`

defined, but also the inner layer`Array`

has a`zip2`

. Can we define a`zip2`

on`[A]?`

that makes use of both of these zip structures? Write the signature of such a function and implement it.

We accidentally put in both part 1 and 2 episodes, so we already solved it before!

Consider the type

`[Validated<A, E>]`

. We again have have a nesting of types, each of which have their own`zip2`

operation. Can you define a`zip2`

on this type that makes use of both`zip`

structures? Write the signature of such a function and implement it.

Let’s start by writing the signature of the function we want to implement:

```
func zip2<A, B, E>(_ a: [Validated<A, E>], _ b: [Validated<B, E>]) -> [Validated<(A, B), E>] {
fatalError()
}
```

If we perform a `zip`

on the arrays, then we will get an array of tuples, where each component of the tuple
is a validated value. We can then `map`

into *that* array, and apply `zip`

on the validated values:

```
func zip2<A, B, E>(_ a: [Validated<A, E>], _ b: [Validated<B, E>]) -> [Validated<(A, B), E>] {
return zip2(a, b).map(zip2)
}
```

This allows us to express the idea of taking two lists of validated values, and combining them into one list, where if there are any errors they combine, and otherwise we get a tuple of the valid values.

Consider the type

`Func<R, [A]>`

. Again we have a nesting of types, each of which have their own`zip2`

operation. Can you define a`zip2`

on this type that makes use of both structures? Write the signature of such a function and implement it.

Let’s start by writing the signature of the function we want to implement:

```
func zip2<A, B, R>(_ a: Func<R, [A]>, _ b: Func<R, [B]>) -> Func<R, [(A, B)]> {
fatalError()
}
```

We know how to perform `zip`

on `Func`

values, so we could start with that:

```
func zip2<A, B, R>(_ a: Func<R, [A]>, _ b: Func<R, [B]>) -> Func<R, [(A, B)]> {
zip2(a, b) // Func<R, ([A], [B])>
fatalError()
}
```

Also, `map`

on `Func`

operates on the second type parameter, in this case `([A], [B])`

, and that’s precisely
the shape we like for `zip`

, so sounds like we can do those two operations together:

```
func zip2<A, B, R>(_ a: Func<R, [A]>, _ b: Func<R, [B]>) -> Func<R, (A, B)> {
return zip2(a, b).map(zip2)
}
```

Finally, conisder the type

`Parallel<Validated<A, E>>`

. Yet again we have a nesting of types, each of which have their own`zip2`

operation. Can you define a`zip2`

on this type that makes use of both structures? Write the signature of such a function and implement it.

Well, now I think you are probably seeing the pattern, but let’s go through the steps anyway. Let’s start by writing out the signature:

```
func zip2<A, B, E>(_ a: Parallel<Validated<A, E>>, _ b: Parallel<Validated<B, E>>) -> Parallel<Validated<(A, B), E>> {
fatalError()
}
```

If we `zip`

both of these parallel values together, we will arrive at another parallel value that holds
two validated values. So, we can `map`

into that new parallel value, and then `zip`

the two validated
values inside it. This implements the function:

```
func zip2<A, B, E>(_ a: Parallel<Validated<A, E>>, _ b: Parallel<Validated<B, E>>) -> Parallel<Validated<(A, B), E>> {
return zip2(a, b).map(zip2)
}
```

This allows us to simultaneously perform two parallel tasks and two validations at the same time, bringing the results into one single value. Very powerful!

Do you see anything common in the implementation of all of the functions in the previous exercise? What this is showing is that nested zippable containers are also zippable containers because

`zip`

on the nesting can be defined in terms of zip on each of the containers.

Every implementation of `zip`

on nested containers looked identical. We first `zip`

the outer containers, then
`map`

on that container with the `zip`

on the inner containers. What we are seeing here is that nested
zippable containers are *always* zippable themselves. Unfortunately Swift does not have the type level
features that allows us to express this algorithm generically, and so we are forced to write the
`zip2(lhs, rhs).map(zip2)`

boilerplate *every* time we want to nest two zippable containers. Maybe someday
this will be better!

And that’s the solutions to the second part of our 3 part
introductory series to `zip`

! If you thought those were too easy, be sure to check out the exercises to
part 3 too. Until next time!

👋 Hey there! If you got this far, then you must have enjoyed this post. You may want to also check out Point-Free, a video series on functional programming and Swift.