The First Cry of Atom Today is the first day of the rest of my life.

Case class for Swift

Last week, Apple new programming language Swift was released. From that time I keep considering Swift looks like Scala language. Scala has two sides as object-oriented-language and functional-programming-language. So there are many features you should learn from scala. One of the most powerful feature of scala is pattern matching. This feature in scala context can be applied to all type objects. It is called constructor pattern matching.

case class User(name: String, age: Int) {}

val u = User("NOBITA", 12)

val ret = u match {
    case User("TAKESHI", 12) => 1
    case User("NOBITA", 12)  => 2
    case User("NOBITA", 13)  => 3
    case _ => 4
}

// ret is 2

All you should do to use pattern match with your custom class is declare case class. The scala compiler generates unapply method called extractors automatically. So you can use these feature very easily.

So Swift is also functional programming language. And it has pattern matchin with switch control flow. But I think it is not sufficient in comparison with Scala.

Therefore I tried it.

//
//  SwiftCase.swift
//  SwiftCase
//
//  Created by Sasaki Kai on 6/19/14.
//  Copyright (c) 2014 Sasaki Kai. All rights reserved.
//

import Foundation


// For matching, unapply method is necessary
protocol SwiftCase {
    func unapply() -> Array<NSObject>
}

class SwiftPair {
    let first: SwiftCase
    let second: AnyObject
    init(first: SwiftCase, second: AnyObject) {
        self.first = first
        self.second = second
    }
}

// DSL like logic for generating pair (Matching object, Result object)
@infix func ~> (source: SwiftCase, target: AnyObject)-> SwiftPair {
    return SwiftPair(first: source, second: target)
}

func match(c: SwiftCase)(arr: Array<SwiftPair>)-> AnyObject? {
    for pair in arr {
        let matchArr = (pair.first as SwiftCase).unapply()
        let originArr = c.unapply()
        var isOk = true
        if matchArr.count == originArr.count {
           for var i = 0; i < matchArr.count; ++i {
                if matchArr[i] != originArr[i] {
                    isOk = false
                }
           }
           if isOk {
              return pair.second
           }
        }
    }
    return nil
}

With this class and functions, you can write below code.

class User: SwiftCase {
    let name: String
    let age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func unapply() -> Array<NSObject> {
        return [self.name, self.age]
    }
}

class OtherUser: SwiftCase {
    let name: String
    let age: Int
    let address: String
    init(name: String, age: Int, address: String) {
       self.name = name
       self.age = age
       self.address = address
    }

    func unapply() -> Array<NSObject> {
        return [self.name, self.age, self.address]
    }
}

let user = User(name: "NOBITA", age: 34)
let ret : AnyObject? = match(user)(arr: [
    User(name: "TAKESHI", age: 23) ~> 1,
    User(name: "NOBITA", age: 32) ~> 2,
    User(name: "NOBITA", age: 34) ~> 3,
    OtherUser(name: "NOBITA", age: 20, address: "TOKYO") ~> 4
])

// ret is 3

Is it looks like matching DSL in Swift? So I want to make this class more sophisticated. Some symbols are not easy to understand and match function need to receive Array parameter. It is not cool ;(

The repository is here. If you have some advice, please let me know. Thank you.