<-- home

Swift Custom Operator(2)

**[Swift: define F#-style pipe-forward ( >) operator that evaluates from left to right.](https://gist.github.com/kristopherjohnson/ed97acf0bbe0013df8af)**
// F#'s "pipe-forward" |> operator
//
// Also "Optional-chaining" operators |>! and |>&
//
// And adapters for standard library map/filter/sorted
infix operator |>   { precedence 50 associativity left }
infix operator |>!  { precedence 50 associativity left }
infix operator |>&  { precedence 50 associativity left }
infix operator |>*  { precedence 50 associativity left }

// Pipe forward: transform "x |> f" to "f(x)" and "x |> f |> g |> h" to "h(g(f(x)))"
public func |> <T,U>(lhs: T, rhs: T -> U) -> U {
    return rhs(lhs)
}

// Unwrap the optional value on the left-hand side and apply the right-hand function
public func |>! <T,U>(lhs: T?, rhs: T -> U) -> U {
    return rhs(lhs!)
}

// If Optional value on left is nil, return nil; otherwise unwrap it and
// apply the right-hand function to it.
//
// (It would be nice if we could name this |>?, but the ? character
// is not allowed in custom operators.)
public func |>& <T,U>(lhs: T?, rhs: T -> U) -> U? {
    return lhs.map(rhs)
}

// Transform "x |>* (f, predicate)" to "f(x, predicate)"
public func |>* <A, B, C, T>(lhs: A, rhs: ((A, (B) -> C) -> T, (B) -> C)) -> T {
    return (rhs.0)(lhs, rhs.1)
}


// Examples
// List of random numbers
let numbers = [67, 83, 4, 99, 22, 18, 21, 24, 23, 2, 86]

// Get the even numbers, sort them in descending order,
// and render the result as a comma-separated string
let result = ", ".join(numbers.filter { $0 % 2 == 0 }
    .sorted { $1 < $0 }
    .map { $0.description })
result

// Was easy for Array, let's try it as a Sequence with free functions
let seq = SequenceOf(numbers)

// All on one line
let seqResult =
    ", ".join(map(sorted(filter(seq){$0 % 2 == 0}){$1 < $0}){$0.description})

// Ugly/unreadable
let seqResultMultiLine =
    ", ".join(
        map(
            sorted(
                filter(seq) { $0 % 2 == 0 }
                ) { $1 < $0 }
            ) { $0.description })

// Readable but verbose/noisy
let filteredNumbers = filter(seq) { $0 % 2 == 0 }
let sortedNumbers = sorted(filteredNumbers) { $1 < $0 }
let numbersAsStrings = map(sortedNumbers) { (n: Int) -> String in n.description }
let commaSeparated = ", ".join(numbersAsStrings)

// Using lazy()
let lazyResult =
    ", ".join(lazy(seq).filter({ $0 % 2 == 0 }).array
        .sorted { $1 < $0 }
        .map { $0.description })


// Can do this with pipe-forward, but need adapters for filter/sorted/map/join.
//
// Each adapter must be curried, and the sequence to be operated on must be the final argument.
// Curried adapter function for Swift Standard Library's filter() function
public func filteredWithPredicate<S : SequenceType>
    (includeElement: (S.Generator.Element) -> Bool)
    (source: S)
    -> [S.Generator.Element]
{
    return filter(source, includeElement)
}

// Curried adapter function for Swift Standard Library's sorted() function
public func sortedByPredicate<S : SequenceType>
    (predicate: (S.Generator.Element, S.Generator.Element) -> Bool)
    (source: S)
    -> [S.Generator.Element]
{
    return sorted(source, predicate)
}

// Curried adapter function for Swift Standard Library's map() function
public func mappedWithTransform<S: SequenceType, T>
    (transform: (S.Generator.Element) -> T)
    (source: S)
    -> [T]
{
    return map(source, transform)
}

let pipeResult =
    seq |> filteredWithPredicate { $0 % 2 == 0 }
        |> sortedByPredicate { $1 < $0 }
        |> mappedWithTransform { $0.description }
        |> String.join(", ")


// Instead of using the adapters, use equivalent closures
let closuresResult =
    seq |> { s in filter(s) { $0 % 2 == 0 } }
        |> { s in sorted(s) { $1 < $0 } }
        |> { s in map(s) { $0.description } }
        |> String.join(", ")


// Tuples
import Foundation

func diagonalLength(width: Double, height: Double) -> Double {
    return sqrt(width * width + height * height)
}

let length = (3, 4) |> diagonalLength

func multiplyAndDivide(multiplier1: Double, multiplier2: Double, divisor: Double) -> Double {
    return multiplier1 * multiplier2 / divisor
}

let value = (10, 20, 50) |> multiplyAndDivide


let evenNumbers = (seq, { $0 % 2 == 0 }) |> filter


// Optional chaining
let elements = [2, 4, 6, 8, 10]

func reportIndexOfValue(value: Int)(index: Int) -> String {
    let message = "Found \(value) at index \(index)"
    println(message)
    return message
}

// Safe to use |>! if we know we'll find a value
find(elements, 6) |>! reportIndexOfValue(6)    // "Found 6 at index 2"
// If left side may be nil, use |>&
find(elements, 3) |>& reportIndexOfValue(3)    // nil
find(elements, 4) |>& reportIndexOfValue(4)    // {Some "Found 4 at index 1"}

// Generic adapters
// The stuff below works, but it's VERY, VERY SLOW in a playground, so
// we've disabled it.  Change the 'false' to 'true' if you want to test it.
#if false
    // Rather than writing those "adapters" for the standard library
    // filter, sorted, and map functions, we can apply a higher-level
    // function to give them the signatures we need.
    // Given a function with signature (A, B) -> T, return curried
    // function with signature (B)(A) -> T
    func pipeAdapted<A, B, T>(f: (A, B) -> T) -> (B -> A -> T) {
        return { b in { a in f(a, b) } }
    }
    let pipeResultWithPipeAdapted =
        seq |> pipeAdapted(filter)({ $0 % 2 == 0 })
            |> pipeAdapted(sorted)({ $1 < $0 })
            |> pipeAdapted(map)({ $0.description })
            |> String.join(", ")
    let pipeStarResult =
        seq |>* (filter, { $0 % 2 == 0 })
            |>* (sorted, { $1 < $0 })
            |>* (map, { $0.description })
            |> String.join(", ")
#endif