JavaFX Tutorials

Monday, October 24, 2016

Disambiguiating Nil Arguments in Swift

Overloading functions in Swift enables you to use the same function name with arguments of different types.  This can lead to an easier-to-use API, especially with code completion.  Instead of having a complement of functions like

  saveMyClass(myclass)
  saveAnotherClass(anotherclass), and
  saveYetAnotherClass(yetanotherclass),

you'd just use a single save() function that would accept the three different types.  The previous example becomes

  save(myclass),
  save(anotherclass), and
  save(yetanotherclass).


However, when using the single named function with Options, you may need to adjust your call.  That's because the keyword "nil" doesn't carry any type information with it.  For instance, given an overloaded method foo() that can take a String? or an Int?

  class MyClass { 
    func foo(person : String?) { 
      print("string version") 
    } 
    func foo(personId : Int?) { 
      print("int version") 
    } 
  } 

  let myobj = MyClass()

will invoke the specific method when passed a non-null argument

 
  10> myobj.foo("Carl")
  string version
   11> myobj.foo(1)
  int version

However, if you attempt to call the method with nil, you'll get an error because Swift can't tell which type of nil this is.

 
  12> myobj.foo( nil )
  repl.swift:12:1: error: ambiguous use of 'foo'
  myobj.foo( nil )
  ^
  repl.swift:2:10: note: found this candidate
    func foo(person : String?) {
         ^
  repl.swift:5:10: note: found this candidate
    func foo(personId : Int?) {
         ^

To resolve the ambiguity, add type information in with the nil argument.  This produces the expected result.

 
  12> myobj.foo( nil as String? )
  string version

In this example, it might have made sense to add a no-arg version of foo() instead of requiring callers to have to think about the nil.  That might not be desirable for longer call signatures that have multiple nilable values.  I've seen this come up recently in an API transition where one "foo" version with older arguments eventually will replace the other, but both remain for compatibility as in foo(ui: NewUIComponent?) replacing foo(ui:OldUIComponent).

No comments:

Post a Comment