Three questions about a more "dynamic" Swift for InfoQ


(sergio) #1

HI all,

a debate has recently taken place within the Objective-C/Swift dev community concerning the lack in Swift of something “equivalent” to Objective-C runtime programming. When using Swift exclusively, there seems to be no easy equivalent for Cocoa fundamental design patterns such as the Responder Chain, NSUndoManager, KVC/KVO/Bindings — and in general for code that leverages Objective-C's dynamic features (i.e., runtime programming).

According to some developers, dynamic features in Ocjective-C grant easy coding and high decoupling and there are concerns that Swift might make harder a number of common Objective-C tasks (e.g., by increasing boilerplate code, lowering readability, making high use of switch, etc.) and eventually increase development time.

In this context, is interesting to know how those concerns are viewed within the Swift dev community and how they could be tackled. Thus, I would like to ask three questions that will be the base for an InfoQ article on the topic:

1. Do you envision a more dynamic Swift? Should some level of dynamism be added at the language or framework level (e.g., to make dynamic dispatch or some form of message passing possible, while ruling out trickier features such as method swizzling and others) possible ? Should Swift be dynamic at all?

2. What is the challenge of making a more dynamic Swift (or adding features to it to help solve the same kind of issues that are solved in ObjC through dynamism)? How could that be achieved without making Swift a less safe language? [e.g., reflection, macros, etc.]

3. Are you aware of any work that has been/is being done in Swift development (Swift 3/Swift 4) to make Swift more capable of addressing the above mentioned concerns?

Thanks a lot in advance!
Sergio

···

Sergio De Simone
https://www.infoq.com/author/Sergio-De-Simone


(Robert Widmann) #2

~Robert Widmann

2016/09/26 16:29、sergio via swift-evolution <swift-evolution@swift.org> のメッセージ:

HI all,

a debate has recently taken place within the Objective-C/Swift dev community concerning the lack in Swift of something “equivalent” to Objective-C runtime programming. When using Swift exclusively, there seems to be no easy equivalent for Cocoa fundamental design patterns such as the Responder Chain, NSUndoManager, KVC/KVO/Bindings — and in general for code that leverages Objective-C's dynamic features (i.e., runtime programming).

According to some developers, dynamic features in Ocjective-C grant easy coding and high decoupling and there are concerns that Swift might make harder a number of common Objective-C tasks (e.g., by increasing boilerplate code, lowering readability, making high use of switch, etc.) and eventually increase development time.

In this context, is interesting to know how those concerns are viewed within the Swift dev community and how they could be tackled. Thus, I would like to ask three questions that will be the base for an InfoQ article on the topic:

1. Do you envision a more dynamic Swift? Should some level of dynamism be added at the language or framework level (e.g., to make dynamic dispatch or some form of message passing possible, while ruling out trickier features such as method swizzling and others) possible ? Should Swift be dynamic at all?

No, and I think moving towards a more dynamic Swift now without as-of-yet-seen significant justification would be a mistake. Swift should be as static as possible. We should be making every attempt to pare down the existing runtime and quietly transition those that rely on dynamism to checked reflection (yes, that is not in fact an oxymoron). The more the compiler knows about your program, the better it does. Period.

2. What is the challenge of making a more dynamic Swift (or adding features to it to help solve the same kind of issues that are solved in ObjC through dynamism)? How could that be achieved without making Swift a less safe language? [e.g., reflection, macros, etc.]

There are very few dynamic patterns that aren't subsumed by the current model. That said, two big ones I know of are Realm's approach of using "runtime-metaprogramming" to derive models and combining static typing with Clojure-style coercible data structures that share a common core. The first can be subsumed by better reflection primitives, the second is more difficult. Static Swift means room for potential optimizations that could destroy the integrity of any system that thinks it knows enough to coerce terms around itself (this kind of programming is powerful precisely because of this knowledge). Perhaps that's not a bad thing. If you know your types ahead of time - which you do if you're trying to perform coercions - you should be able to write down something to convince the type checker. If you can't, it's an expressiveness problem we should deal with at the level of the type system.

···

Thanks a lot in advance!
Sergio

Sergio De Simone
https://www.infoq.com/author/Sergio-De-Simone
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Benjamin Spratling) #3

NSUndoManager
Generic, block-based undo would be much better in Swift than the old NSUndoManager. Certainly, “invocation-based” undo is just plain gone in Swift, and not necessary in Obj-C. I’m toying with a from-scratch new undo manager that dynamically persists a cache of large amounts of data that are filling up RAM. If you’re interested, maybe I’ll post it on git hub.

Responder Chain
The easy way around Swift’s lack of a “respondsTo" is for a controller to provide a reference to the “next” controller in the chain. Each controller class optionally conforms to a protocol, and the protocol would have the method the control wants to call. I have used this extensively, and it works pretty well. I’ve also discovered that a more “iOS” way of building UIs reduces the levels of disconnection between the Control and the Controller that was present in OS X, eliminating the problem in most cases.

KVO was my favorite way to do things in Obj-C; I even wrote some helper classes to prevent the pesky “you didn’t clean up your observers before dealloc” thing. (I’m using ARC, quit bothering me with that already!) Unfortunately, Swift’s init/deinit order made those classes impossible (as far as I can tell), so I ditched all KVO. In the last year, developing 5 apps in pure Swift, I haven’t needed KVO, but I haven’t been working closely with AV modules, which used KVO as its preferred pattern, so I’m not sure how cumbersome the became. For the one thing I did use AV- for, there was a closure-based API, and there wasn’t an issue. So I’m preferring closures. It would be nice (sigh) to use a more declarative approach. I’ve recently noticed “ReativeSwift”, but haven’t had time to evaluate if it saves me work.

The one thing I wish I did have in the KVO genre is a callback when a weak var went to nil. For example, reactively removing items from the UndoManager stack when the controller it references is deinit’d. The iOS way for doing this is for the controller to own the undo manager

KVC is inherently unsafe in Obj-C, and Swift is all about the safety. Really all that’s happened, as best I can tell, is I have to write explicit adapters to create objects. Making a protocol that implements a init?(json:JSON) method, and storing a reference to the protocol and calling it as needed isn’t that bad. So my need for KVC has disappeared. I get that bindings were a little different, but I never got that in to bindings in the first place. It seemed like a whole lot of guesswork and non-safe “stringly-typed” code. I much prefer knowing at the call site what my types need to be. (CoreImage drives me nuts).

So I think in general, I need to see with more compelling use case than the ones listed for warrant the kind of dynamic features you’re talking about. Part of the benefit of Swift to me is the safety. Taking the time to write my code in a safer way is a large part of the task of making it safe in the first place, but now, the compiler can catch accidental mistakes.

If anything, I’d like to be able to dependency-inject data when a controller is unarchived from a .nib, for instance.

-Ben Spratling

···

On Sep 26, 2016, at 3:29 PM, sergio via swift-evolution <swift-evolution@swift.org> wrote:

HI all,

a debate has recently taken place within the Objective-C/Swift dev community concerning the lack in Swift of something “equivalent” to Objective-C runtime programming. When using Swift exclusively, there seems to be no easy equivalent for Cocoa fundamental design patterns such as the Responder Chain, NSUndoManager, KVC/KVO/Bindings — and in general for code that leverages Objective-C's dynamic features (i.e., runtime programming).


(Johannes Neubauer) #4

~Robert Widmann

2016/09/26 16:29、sergio via swift-evolution <swift-evolution@swift.org> のメッセージ:

HI all,

a debate has recently taken place within the Objective-C/Swift dev community concerning the lack in Swift of something “equivalent” to Objective-C runtime programming. When using Swift exclusively, there seems to be no easy equivalent for Cocoa fundamental design patterns such as the Responder Chain, NSUndoManager, KVC/KVO/Bindings — and in general for code that leverages Objective-C's dynamic features (i.e., runtime programming).

According to some developers, dynamic features in Ocjective-C grant easy coding and high decoupling and there are concerns that Swift might make harder a number of common Objective-C tasks (e.g., by increasing boilerplate code, lowering readability, making high use of switch, etc.) and eventually increase development time.

In this context, is interesting to know how those concerns are viewed within the Swift dev community and how they could be tackled. Thus, I would like to ask three questions that will be the base for an InfoQ article on the topic:

1. Do you envision a more dynamic Swift? Should some level of dynamism be added at the language or framework level (e.g., to make dynamic dispatch or some form of message passing possible, while ruling out trickier features such as method swizzling and others) possible ? Should Swift be dynamic at all?

No, and I think moving towards a more dynamic Swift now without as-of-yet-seen significant justification would be a mistake. Swift should be as static as possible. We should be making every attempt to pare down the existing runtime and quietly transition those that rely on dynamism to checked reflection (yes, that is not in fact an oxymoron). The more the compiler knows about your program, the better it does. Period.

I do not agree (fully). Although static type checking is great, object orientation is too. Dynamic dispatch and therefore (subtype) polymorphism is a wonderful thing. Going down the road to make every call static is an extreme that won't help anybody. As always the right balance makes the deal.

All the best
Johannes

···

Von meinem iPhone gesendet

Am 26.09.2016 um 23:32 schrieb Robert Widmann via swift-evolution <swift-evolution@swift.org>:

2. What is the challenge of making a more dynamic Swift (or adding features to it to help solve the same kind of issues that are solved in ObjC through dynamism)? How could that be achieved without making Swift a less safe language? [e.g., reflection, macros, etc.]

There are very few dynamic patterns that aren't subsumed by the current model. That said, two big ones I know of are Realm's approach of using "runtime-metaprogramming" to derive models and combining static typing with Clojure-style coercible data structures that share a common core. The first can be subsumed by better reflection primitives, the second is more difficult. Static Swift means room for potential optimizations that could destroy the integrity of any system that thinks it knows enough to coerce terms around itself (this kind of programming is powerful precisely because of this knowledge). Perhaps that's not a bad thing. If you know your types ahead of time - which you do if you're trying to perform coercions - you should be able to write down something to convince the type checker. If you can't, it's an expressiveness problem we should deal with at the level of the type system.

Thanks a lot in advance!
Sergio

Sergio De Simone
https://www.infoq.com/author/Sergio-De-Simone
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ricardo Parada) #5

How would you implement something like Core Data in pure Swift on the server where the Obj-C runtime is not available?

···

On Sep 26, 2016, at 5:32 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

No, and I think moving towards a more dynamic Swift now without as-of-yet-seen significant justification would be a mistake. Swift should be as static as possible. We should be making every attempt to pare down the existing runtime and quietly transition those that rely on dynamism to checked reflection (yes, that is not in fact an oxymoron). The more the compiler knows about your program, the better it does. Period.


(Chris Lattner) #6

HI all,

a debate has recently taken place within the Objective-C/Swift dev community concerning the lack in Swift of something “equivalent” to Objective-C runtime programming. When using Swift exclusively, there seems to be no easy equivalent for Cocoa fundamental design patterns such as the Responder Chain, NSUndoManager, KVC/KVO/Bindings — and in general for code that leverages Objective-C's dynamic features (i.e., runtime programming).

Yep, many of us saw it. Thank you for asking on swift-evolution, which is the proper place to discuss the future of Swift.

According to some developers, dynamic features in Ocjective-C grant easy coding and high decoupling and there are concerns that Swift might make harder a number of common Objective-C tasks (e.g., by increasing boilerplate code, lowering readability, making high use of switch, etc.) and eventually increase development time.

In this context, is interesting to know how those concerns are viewed within the Swift dev community and how they could be tackled.

This is just my personal opinion, not meant to represent that of the entire core team:

1. Do you envision a more dynamic Swift? Should some level of dynamism be added at the language or framework level (e.g., to make dynamic dispatch or some form of message passing possible, while ruling out trickier features such as method swizzling and others) possible ? Should Swift be dynamic at all?

Yes, I personally think that adding “dynamic” features to Swift is absolutely essential. That said, one of the things I’ve observed in the “blogger debate” that happened a few months ago is that everyone has different ideas of what “dynamic” means. I don’t think that it is interesting to provide the “equivalent” features to Objective-C, but I do think it is important that Swift be able to solve the same sorts of problems (including your list of Responder Chain, NSUndoManager, KVC/KVO/Bindings, …) in an fluent/expressive way, even if it works differently.

In fact, one piece of this was the first candidate listed in the Swift 4 stage 2 goals, adding a reflection API:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160725/025676.html
This included the statement: "The core team is committed to adding powerful dynamic features to Swift."

The primary reason that Swift currently lacks these features is simple prioritization. It has unfortunately not been the highest priority to build, because we already have working systems for all of those in Cocoa, and things like source stability and binary stability (among other things) are more important to the Swift community in the moment. As Swift evolves, they will bubble up in the priority list.

I know that having a read/write data reflection API is the top of the list for many people, and having the ability to reflect on methods is also important for some patterns. I look forward to the community having the bandwidth to properly design and debate these things, because I’m confident that there are good answers for these.

That said, to be clear, this is definitely out of scope for Swift 4 Stage 1, which is our current mode. We need to stay focused in the short term on the most pressing issues, which revolve around Source and ABI stability.

2. What is the challenge of making a more dynamic Swift (or adding features to it to help solve the same kind of issues that are solved in ObjC through dynamism)? How could that be achieved without making Swift a less safe language? [e.g., reflection, macros, etc.]

It is a bit pedantic, but Swift isn’t a “safe" language: it is a “safe by default” language which gives you direct access to unsafe constructs when you explicitly ask for them (e.g. UnsafePointer).

Regardless of that, I don’t see any conflict with adding reflective capabilities, and I don’t see how having them would present a safety problem. I expect that they’d end up being somewhat different than the Objective-C analogues of these features, but that is true about everything else in Swift (e.g. method call syntax in the two languages is pretty different).

3. Are you aware of any work that has been/is being done in Swift development (Swift 3/Swift 4) to make Swift more capable of addressing the above mentioned concerns?

The most relevant is the extensive work on data reflection metadata that was put into Swift 3. It is key to enabling the Xcode 8 memory visualization / debugger feature, and is a fundamental building block for a data reflection API. In fact, if I recall correctly, a 3rd party framework already build a custom data reflection API around this metadata already (Zewo?).

-Chris

···

On Sep 26, 2016, at 1:29 PM, sergio via swift-evolution <swift-evolution@swift.org> wrote:


(Robert Widmann) #7

Statically typed persistent storage solutions are far and away superior to Core Data. Whether you’re looking for queries that are correct-by-construction with DSLs or looking for easy model construction and composability, or even just modularity and ease of use. The ability to have the type system check your work is not antithetical to the idea of marshaling data - if anything it’s far more effective.

- Begin with a protocol for serializable things to teach the framework about your schema:

/// decode • encode == id
public protocol Serializable {
  /// Encode a value.
  var serialize : Put<Format> { get }
  /// Decode a value.
  static var deserialize : Get<Self> { get }
}

- Provide combinators and functions attached to `Get` and `Put` to make [de]serializing aggregates easy.

struct Person : Equatable {
  let age : UInt32
  let weight : UInt32
}

extension Person : Serializable {
  static var deserialize : Get<Person> {
    return Get.zip(
      UInt32.deserialize,
    ).map(Person.init)
  }

  var serialize : Put<Person> {
    return self.age.serialize
      .then(self.weight.serialize)
  }
}

- Select a backend

public struct SqlBackend : Backend {
    func prepare(statement : String) -> Statement { }
  func insert<Entity : Serializable>(_ entity : Entity) -> Result { }
  // etc.
}

- Maybe put a little DSL on top if you’re feeling cheeky

let p : SQLQuery<[Person]> =
  select <|
  from { p in
    (where_ <| exists <|
      from { (p : Person) -> SQLQuery<()> in
        where_(val(p.age) <= val(18))
      }).then(SQLQuery<Person>.pure(p))
}

- Serve immediately

···

On Sep 27, 2016, at 12:29 PM, Ricardo Parada <rparada@mac.com> wrote:

On Sep 26, 2016, at 5:32 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

No, and I think moving towards a more dynamic Swift now without as-of-yet-seen significant justification would be a mistake. Swift should be as static as possible. We should be making every attempt to pare down the existing runtime and quietly transition those that rely on dynamism to checked reflection (yes, that is not in fact an oxymoron). The more the compiler knows about your program, the better it does. Period.

How would you implement something like Core Data in pure Swift on the server where the Obj-C runtime is not available?


(Robert Widmann) #8

* Up to errors in my pseudo-code, of course.

  static func deserialize : Get<Person> {
    return Get.zip(
      UInt32.deserialize,
      UInt32.deserialize
    ).map(Person.init)
  }

···

On Sep 27, 2016, at 12:44 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

UInt32.deserialize,