10

Flush with excitement from extending Bool, I thought it would be fun to extend closures in Swift (we did this with no fuss at all in Smalltalk, so why not?).

Here's my playground:

typealias NiladicClosure = () -> ()

extension NiladicClosure {
    var theAnswerToLife:Int {
        return 42
    }
}

let block:NiladicClosure = {}

block.theAnswerToLife

It doesn't work, saying that NiladicClosure does not have a member named 'theAnswerToLife'. Looking in the console, I get a bit more information:

Playground execution failed: /var/folders/2k/6y8rslzn1m95gjpg534j7v8jzr03tz/T/./lldb/33726/playground119.swift:3:1: error: non-nominal type 'NiladicClosure' cannot be extended
extension NiladicClosure {
^         ~~~~~~~~~~~~~~

What is a non-nominal type? Is there a pattern/workaround?

Other similar questions predated Swift 2, also were specific enough that people offered workarounds to the specific extension. I'm interested in whether Swift closures are first class objects that I can add additional behavior to, like other things in Swift.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • 1
    You cannot extend closures. Compare http://stackoverflow.com/questions/28317625/can-i-extend-tuples-in-swift (which is about extending tuples, but the same answer applies to functions/closures). – Martin R Jun 18 '15 at 21:49
  • Any idea _WHY_ tuples and closures are not extensible? In the case of closure, is it because closures are never actually reified with an actual object (or struct)? (at least, I can't see anywhere where they do) – Travis Griggs Jun 18 '15 at 21:53
  • 1
    @TravisGriggs It's "[mostly an implementation limitation](https://twitter.com/jckarter/status/611656674391097344)". – Aaron Brager Jun 18 '15 at 22:08

1 Answers1

8

What is a non-nominal type?

A nominal type is a type with an explicit name. A non-nominal type is a type without such a name, like () -> (). Compound types, including closures and tuples (such as (Int, String)) cannot be extended.

Is there a pattern/workaround?

You could use composition instead of extensions, perhaps using Swift 2's new protocol features:

typealias NiladicClosure = () -> ()

protocol NiladicClosureProtocol {
    var someClosure : NiladicClosure? {get}
}

protocol SorryForTheInconvenience {
    var theAnswerToLife : Int {get}
}

extension SorryForTheInconvenience {
    var theAnswerToLife : Int {
        return 42
    }
}

struct SomethingAwesome : NiladicClosureProtocol, SorryForTheInconvenience {
    var someClosure : NiladicClosure?
}

let foo = SomethingAwesome()
foo.theAnswerToLife // 42
Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • "You could use composition instead of extensions, perhaps using Swift 2's new protocol features." I'm not sure how that would work. I could make an `AnswersTheBigQuestion` protocol, but I wouldn't be able to extend the `non-nominal` type to adopt that protocol, would I? Since you apparently can't extend them. – Travis Griggs Jun 18 '15 at 21:48
  • @TravisGriggs I just added an example. You could make a struct that *has* a `NiladicClosure`, instead of making something that *is* a `NiladicClosure`. – Aaron Brager Jun 18 '15 at 21:50
  • It would be nice if `SomethingAwesome` (or other implementations of `NiladicClosureProtocol`) could be made to feel like an actual closure, i.e. if we could call `foo()` itself instead of `foo.someClosure()`. In other languages (C++, Scala, Kotlin, ...), we could define an `()` operator, but Swift doesn't allow that. :-( – jcsahnwaldt Reinstate Monica Jun 08 '23 at 11:58