Swift 2.0-reverse() for collections is not composable

I'm finding that in Xcode 7 Beta and trying out Swift 2.0 that the reverse function is not composable directly so have to write a wrapper function to make it so . eg. for the checksum function in Airspeed in Swift 2.0. Pls see mapReverse function


http://airspeedvelocity.net/2014/12/03/a-straw-man-argument-for-more-trying-functional-programming-in-swift/


func checksum(ccnum: String) -> Bool {

    let toInt = { (c: Character)->Int? in Int(String(c)) }
    let doubleAndCombine = { i in
        i < 5
            ? i * 2
            : i * 2 - 9
    }

    return ccnum.characters
        |> { mapSome($0, transform: toInt) as [Int] }
        |> { mapReverse($0)}
        |> { mapEveryNth($0, n: 2, transform: doubleAndCombine) as [Int] }
        |> sum
        |> isMultipleOf(10)

}
func mapReverse
    <S: SequenceType>
    (source: S) -> [S.Generator.Element] {
        return source.reverse()
}

Can you please specify what it is that you want to know? (I can only see statements and no question.)

Accepted Answer

With more protocols in Swift, it looks like the idiomatic way to do function composition (or rather, something like it) is to use methods defined on the protocols you're using. You'll get syntax that's actually a lot cleaner than what's above, and it'll be faster. In Swift 1, you would have had to write something like:

reverse(filter(map(sequence, transform: {$0 * 2}), isElement: {$0 % 3 != 0}))

In which case the pipe forward operator makes sense, to simplify it to:

sequence
  |> map    {$0 * 2}
  |> filter {$0 % 3 != 0}
  |> reverse

But, with Swift 2, all of those old functions are now methods, so you can just do:

sequence
  .map    {$0 * 2}
  .filter {$0 % 3 != 0}
  .reverse()


And your example, using protocol extensions, looks like this:


extension SequenceType {
  func mapNth(n: Int, transform: Generator.Element -> Generator.Element) -> [Generator.Element] {
    var i = 0
    return self.map{ ++i % n == 0 ? transform($0) : $0 }
  }
}

extension SequenceType where Generator.Element : IntegerArithmeticType {
  func sum() -> Generator.Element {
    return self.reduce(Optional<Generator.Element>()) {
      accu, element in accu.map { $0 + element } ?? element
    }!
  }
}

extension Int {
  func isMultipleOf(n: Int) -> Bool {
    return (self % n) == 0
  }
}

func checksum(ccnum: String) -> Bool {

  let toInt = { (c: Character)->Int? in Int(String(c)) }

  let doubleAndCombine = { i in
    i < 5
      ? i * 2
      : i * 2 - 9
  }

  return ccnum
    .characters
    .flatMap(toInt)
    .reverse()
    .mapNth(2, transform: doubleAndCombine)
    .sum()
    .isMultipleOf(10)
}

checksum("4012 8888 8888 1881") // true

Awesome! I now understand how adding methods to protocols can help in composition and writing cleaner code. Also this way I can also build project domain specific methods on the protocols.


Thanks again!

And the most current post on Airspeed Velocity is, appropriately, http://airspeedvelocity.net/2015/06/23/protocol-extensions-and-the-death-of-the-pipe-forward-operator/

Swift 2.0-reverse() for collections is not composable
 
 
Q