NK

Using monads in Swift

One of the coolest features of Swift is that it lets you define your own operators. This leads to being able to re-implement many operators found in functional languages that we know and love.

Swiftz is a new library that implements a handful of essential and obscure operators, as well as many structs that we take for granted in functional environments. Today I’m going to take a brief look at the Maybe monad provided by Swiftz.

Let’s implement a game of chinese whispers. But in this version, any participant can give up on the game. We have the participants, each a function that might return a phrase that they think is correct, or nothing at all:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func participant1(phrase: String) -> Maybe<String> {
  return Maybe.just(phrase)
}

func participant2(phrase: String) -> Maybe<String> {
  return Maybe.just("aloha")
}

func participant3(phrase: String) -> Maybe<String> {
  return Maybe.just("\(phrase) i think")
}

func participant4(phrase: String) -> Maybe<String> {
  return Maybe.none()
}

Now, how do we write a function that takes a phrase and an ordered list of participants and then returns the result? We could do this:

1
2
3
4
5
6
7
8
func play(initialPhrase: String, participants: [String -> Maybe<String>]) {
  var result: Maybe<String> = Maybe.just(initialPhrase)
  for participant in participants {
    if result.isNone() { break }
    result = participant(result.fromJust())
  }
  return result
}

You’ll notice that we are checking for isNone() on each iteration because we don’t want to pass the next participant anything if the game has already stopped. This is a problem that Monad’s solve, given the context of Maybe it knows how to handle the case of nothing or something:

1
2
3
4
5
6
7
8
9
func play(initialPhrase: String, participants: [String -> Maybe<String>]) {
  var result: Maybe<String> = Maybe.just(initialPhrase)
  for participant in participants {
    // >>- is the equivalent of >>= from haskell, it's
    // called the `bind` operator
    result = result >>- participant
  }
  return result
}

Okay. That saved about one line, but we can make the entire thing more functional:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func play(initialPhrase: String, _ transform: String -> Maybe<String>) {
  return transform(initialPhrase)
}

// And usage looks like:
play("hello there", { phrase: String -> Maybe<String> in
  return pure(phrase)
         >>- participant1
         >>- participant2
         >>- participant3
})

// Or: 
play("hello there, ((((>>-participant1)
                       >>-participant2)
                       >>-participant3)  Maybe.just))
                       // • is an operator defined by Swiftz (compose)