The Swift wish list

I already put down the biggest missing features in Beta 5 that I hoped we might see in Beta 6. Unfortunately none of them surfaces, and it’s looking like the Swift team is locking down for the 1.0 release.

Given that most of those missing features are real important, they should obviously be fixed first:

  • Non generic subclasses to generic classes
  • Safe unwrapping with let-else
  • Minimum expected enum functionality for raw enums
  • Default functions for protocols
  • Extending a reduced subset of a generic class

Still, I can’t help myself from presenting a longer wish list for Swift, with additional possible improvements – in no particular order:

Prefixes for ivars

I already blogged about this: “The case for [even an ugly] ivar prefix” where I suggested $ as an ivar prefix. More discussion on the dev forum seemed to confirm that “$” is the best candidate – it has widespread usage as prefix in other languages and is already in use for closure short syntax ($0, $1, $2 …)

Overall better reflection

It’s possible get reflection in Swift today if you inherit from NSObject, but it’s very limited as structs, enums etc aren’t compatible with ObjC.

Optional protocols in pure Swift

Today you need to run all optional protocols through ObjC by using @objc. This means the protocol functions can’t use anything Swift specific – which severely limits its use except as ObjC glue. Optional protocols is a very nice feature and it would be great to have a 100% Swift compatible solution.

Require “required” on implemented interface methods

Today we have to add “required” when implementing initializers required by protocols, but for remaining protocol methods there is no requirement. Requiring (no pun intended) the code to annotate protocol implementations with “required” would help avoiding errors, especially when working with optional protocols:

@objc protocol Foo {
  optional func bar() 
}

// Today:
class Baz : Foo {
  func bar() { println("Hello") }
}

// With mandatory "required":
class Baz: Foo {
  required func bar() { println("Hello") }
}

class Baz : Foo {
  func bar() { println("Hello") }
  ^----- Error, interface method must be marked as "required"
}

class Baz : Foo {
  required func bor() { println("Hello") }
  ^----- Error, function bor does not implement a protocol method
}

The advantages should be obvious.

Use object + unbound method instead of selector or closure

ObjC relies heavily on selectors. The downside is that a selector doesn’t reveal neither the return nor parameter types. On the other hand we have closures – which are real nice for delayed actions or in-place functions, but which easily create retain cycles. Although this is easier to avoid that before with Swift’s unowned syntax, it’s still not ideal.

The middle point between those two extremes is using object + unbound method. An unbound method is almost a selector in terms of what it retains, and yet it is fully typesafe.

If we look at NSNotificationCenter as an example, we could add the following method:

func addObserver<C : AnyObject>(
  observer : C, 
  method: (C) -> (NSNotification) -> Void, 
  name: String, 
  object : AnyObject?)

That method would allow us to use the same type of functions as with the selector, with same memory retention guarantees, but with 100% type safety:

NSNotificationCenter.defaultCenter().addObserver(
  foo, 
  method:Foo.notificationCallback, 
  name: SomeNotificationName, 
  object: nil)

Unfortunately there is no simple way of extending object + unbound method to replace selectors. Although it is possible to extend any method taking a closure to take target + unbound method, you end up losing many of the advantages you could gain with custom support for it.

The whole concept could be made even more attractive if we had tools like extracting object + unbound method from a bound method, and had closures (or at least unbound functions) implement hashable.

One line returns

Today we can write guard clauses in a single line, but they look like dirty shortcuts:

if foo > 1 { return false }

It would be neat to have a single line return with a distinct syntax, e.g.

if foo > 1 => return false

The real advantage is that will fit better with other proposed syntax additions, for example let-else:

let foo = foo else => return false
//vs
let foo = foo else { return false }

and with switch-as-expression.

Macros through pseudo closures

Adding an annotation similar to autoclosure that changes the semantics of a closure to:

  1. return would mean return in the outer scope
  2. continue would mean return from the closure (and consequently may take an argument!)
  3. break would mean return from the function

More about this here: https://swiftopinions.wordpress.com/2014/08/22/macro-like-syntax-extensions-through-pseudo-closures-a-syntax-proposal/

Union types in generics and return values

Being able to define generics that take a union of types:

class Foo<T : Int | Double> { }

And also being able to specify two or more distinct return types for the same function e.g.

func foo() -> Int | Double { /* ... */ }

Generics with separate covariance / contravariance

Consider an array of type Array<AnyObject> it is not safe to cast this to any particular type because of this:

class Foo<T> {
   var val : T
   init(val: T) { self.val = val }
}

var a = Foo<AnyObject>(val: Baz())
var b = a as Foo<Baz> // Disallowed because:
a.val = Bar() // Would break type safety in b

Nor is the opposite possible:

var a = Foo<Baz>(var: Baz())
var b = a as Foo<AnyObject> // Disallowed because
b.val = Bar() // Would break type safety in a 

The problem is that reading and writing transform differently. Although casting from Foo<AnyObject> to Foo<Baz> is safe for writing it is not safe for reading. Conversely, casting from Foo<Baz> to Foo<AnyObject> is safe for reading but not for writing.

The solution, found in other languages, is to allow for casts that work differently depending on whether we set or get a value. So we could cast Foo<AnyObject> to Foo<Baz> as long as we only perform writes on it. And for the other cast we could do it with the restriction that we only can read values.

Switch as an expression

This would allow code like this:

let someVar = switch type {
  case .Foo: "Foo"
  case .Bar: "Bar"
}

This is already almost possible with closures:

let someVar = { switch type {
  case .Foo: return "Foo"
  case .Bar: return "Bar"
} }()

But we are unable to succinctly express things like:

let someVar = switch type {
  case .Foo: "Foo"
  case .Bar: "Bar"
  default:
     println("Unexpected value, exiting early")
     return ""
}
return prettyPrint(someVar)

 

Clearer syntax for Optional dispatch

I’ve already argued for a clearer optional dispatch that does away with !/? except where they are easily read. For other cases I would like to see a syntax that is visually distinct from normal dispatch so that the branching is clear:

// A
foo.bar?().doSomething()
foo.bar()?.doSomething()
// B
foo.bar ? ().doSomething()
foo.bar() ?. doSomething()
// C
foo.bar->().doSomething()
foo.bar()->doSomething()

The branching point is clear in examples B & C, not so much in A – at least not at a quick glance.


 

What would you like to see the most? Leave a comment!

8 thoughts on “The Swift wish list

  1. Complex Types, plus support for complex functions equivalent to Doubles. 2DCompex arrays are used extensively in scientific programming. I know of at least one available on github, but I have found no way to use it in 2D complex vDSP functions(1D yes, but not 2D).
    Plus an easy way to access Gnuplot via pipes,such as in “C”. I have tried several ways but no luck. This may be due to my lack of knowledge of Objc, which I never liked.

    Like

    • One line returns on the same line. The reason for goto fail is when you allow one line returns with line break.

      if (foo) return; // Sane code 
      
      if (foo)
            return;  // Setting yourself up for a world of pain
      
      

      One line returns would require the first style. I would never in my life suggest to allow the second.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s