Skip to content

Instantly share code, notes, and snippets.

@tmspzz
Last active June 2, 2017 19:26
Show Gist options
  • Save tmspzz/53f9568617654c38a219dd4a8353d935 to your computer and use it in GitHub Desktop.
Save tmspzz/53f9568617654c38a219dd4a8353d935 to your computer and use it in GitHub Desktop.
Add Tuple unsplatting - Removed by SE-0110

Add Tuple unsplatting

As a side effect of SE-0110 tuple unsplating was removed from the language.

While this claims to make tooling like the type checker faster, it deals quite a blow to expressivity.

Filterting dictionaries is just an example and maybe not the best. However I hope the point gets across. Let's compare to other languages.

Examples of pattern matching in closure parameters:

Swift

let eighteenOrMore = ["Tom" : 33, "Rebecca" : 17, "Siri" : 5].filter { arg in
  let (_, age) = arg // Awkward :| No tuple unsplatting
  return age >= 18
}

// OR

// Still No tuple unsplatting
let eighteenOrMore = ["Tom" : 33, "Rebecca" : 17, "Siri" : 5].filter { $0.1 >= 18 } 

// OR

// Somewhat better
let eighteenOrMore = ["Tom" : 33, "Rebecca" : 17, "Siri" : 5].filter {(arg: (name: String, age: Int)) in arg.age >= 18 }

Kotlin

mapOf("Tom" to 33, "Rebecca" to 17, "Siri" to 5).filter({ (_, age) -> age >= 18 })

Python 2.7

{name: age for name, age in {'Tom': 33, 'Rebecca': 17, 'Siri': 5}.iteritems() if age >= 18 }

Ruby

{"Tom" => 33, "Rebecca" => 17, "Siri" => 5}.select{ |_, age| value >= 18 }

Haskell

filter (\(_,age) -> age>= 18) [("Tom", 33), ("Rebecca", 17), ("Siri", 5)]

Clojure

(filter (fn [[_ age]] (>= age 18)) {"Tom" 33 "Rebecca" 17 "Siri" 5})

Scala

List(("Tom" , 33), ("Rebecca", 17), ("Siri", 5)).filter{ case (_, age) => age >= 18}

Rust

[("Tom", 33), ("Rebecca", 17), ("Siri", 5)].into_iter().filter(|&&(_, age)| age >= 18);

JavaScript

Object.entries({Tom: 33, Rebecca: 17, Siri: 5}).filter(([_, age]) => value >= 18)

Erlang

[T || {_, Age} = T <- [{"Tom", 33}, {"Rebecca", 17}, {"Siri", 5}], Age > 18].

CLisp

(remove-if-not #'(lambda (x) (>= (cadr x) 18)) '(("Tom" 30) ("Rebecca" 17) ("Siri" 5)))
@tmspzz
Copy link
Author

tmspzz commented May 25, 2017

@AliSoftware I think I will have other occasions for coffee and cat videos so I'm all up for speed while keeping expressivity :)

@AliSoftware
Copy link

AliSoftware commented May 25, 2017

Define this:

prefix operator *

prefix func *<A1,A2,R>(f: @escaping  (A1, A2) -> R) -> ((A1, A2)) -> R {
  return { (tuple: (A1, A2)) -> R in
    f(tuple.0, tuple.1)
  }
}

prefix func *<A1,A2,A3,R>(f: @escaping  (A1, A2, A3) -> R) -> ((A1, A2, A3)) -> R {
  return { (tuple: (A1, A2, A3)) -> R in
    f(tuple.0, tuple.1, tuple.2)
  }
}

// We could add some for tuples with 4 items, but I don't think people have a lot of real-world cases with tuples with more than 4 items (at that point, create a struct already)

Then even after SE-110 you can:

func foo(_ x: Int, _ y: Int) -> Int {
  return x + y
}

[(1,2),(3,4),(5,6)].map(*foo) // valid even after SE-110

let o1 = Observable<Int>.from(1,2,3)
let o2 = Observable<Int>.from(4,5,6)
Observables.zip(o1, o2, combine: *foo)

Note that, even before SE-110, in Swift 3 you were already not able to do this anymore:

let tuple = (1,2)
foo(tuple)
// error: passing 2 arguments to a callee as a single tuple value has been removed in Swift 3
// foo(tuple)
// ^  ~~~~~~~

So I'm not sure why people are so upset with SE-110 being applied to closures while the exact same rule was already applied to functions but nobody rioted back then ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment