Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Plorkyeran posted:

Defer puts the cleanup code next to the code creating the thing which needs to be cleaned up rather than as far away as possible (and is pretty useful even in the absence of exceptions).

try-finally works in the absence of exceptions too (such as simply returning), but the code-proximity argument is a good one.

Adbot
ADBOT LOVES YOU

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
It works, but using try-finally just for cleanup from multiple returns would be weird and misleading.

Toady
Jan 12, 2009

A prerelease version of the Swift 2 book is available.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Subjunctive posted:

rjmccall: would love to know more about why you chose Go-style defer over try-finally. I'd have expected the latter!

Basically, three reasons. (Also, in this particular case the "you" is specific: this is primarily my design.)

Code proximity is the first. In the common case where a cleanup is mandated by some specific action, you can immediately see that the cleanup is necessary instead of having to carefully analyze the rest of the control flow from that point. Now, this advantage is in inherent tension with code sequencing, because defer makes the compiler inject implicit code along exits from the scope, whereas finally appears in its proper order if you think of the jump as passing through the finally block. However, that's a bit of a leap, and I don't think it matches the way most programmers really think about it, at least not for jumps other than fall-through (e.g. return); they just think of the finally block executing, rather than thinking specifically about control flowing forward in the program. And that's fair, because finally is still pretty magical; the only code structure that's really 100% faithful to the execution flow is crappy, error-prone code duplication.

The second is that try/finally doesn't really get the scoping right. Suppose you create something, and you want to make sure it gets destroyed. In the common acquire/release pattern, there's often a variable whose scope should exactly match the lifetime of the resource. try introduces a scope, but you can't declare the variable in it, because it's out of scope in the finally and because you don't actually want to release if there was an error during acquisition. So you declare the variable outside the scope, which breaks the property we want. You could use a C#-like using feature instead, but then the cleanup is no longer ad hoc (you have to make a disposable type) and it's not always a clean fit; that doesn't mean we for-sure will never add that feature, but it's not as core as defer.

The third is that finally doesn't really compose very well. defer actions just pile up as statements in a single scope, but finally blocks have to be nested — either that, or you have to hoist variables and conditionalize the logic in the finally, and potentially even add more finally blocks there.

Choadmaster
Oct 7, 2004

I don't care how snug they fit, you're nuts!
Second year in a row that Swift is by far the most exciting thing at WWDC. Thanks!

toiletbrush
May 17, 2010
All awesome stuff, althought I thought there was a typo in some docs when it talked about generics in ObjC...until I found out...you added generics to ObjC?!

Simulated
Sep 28, 2001
Lowtax giveth, and Lowtax taketh away.
College Slice
The new availability stuff is interesting; I'm curious why the statement version looks like a macro.

It also prevents you from adopting something like UISearchController on iOS 8 while supporting iOS 7 because you can declare protocol conformance to the new protocol but can't actually create a valid class that implements the protocol. That seems like an ongoing problem given how UIKit is typically updated and evolved. It would be solved by declaring adoption of the protocol to be based on availability but I'm not sure if that's the best way.

Maybe the answer is just to have subclasses for different platform versions?

Kallikrates
Jul 7, 2002
Pro Lurker
rjmccall on stage!

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Kallikrates posted:

rjmccall on stage!

:syoon:

Star War Sex Parrot
Oct 2, 2003

KidDynamite
Feb 11, 2005

Is that rjmcall with the teapot shirt?

lord funk
Feb 16, 2004

KidDynamite posted:

Is that rjmcall with the teapot shirt?

Yeah and that shirt is great. Nice presentation!

KidDynamite
Feb 11, 2005

Awesome I came in late but that was a great presentation from what I saw.

Simulated
Sep 28, 2001
Lowtax giveth, and Lowtax taketh away.
College Slice
Agreed; excellent presentation and excellent Swift changes.

rjmccall: it was nice to shake your hand right before the coordinator person shooed us away.

Kallikrates
Jul 7, 2002
Pro Lurker

Ender.uNF posted:

Agreed; excellent presentation and excellent Swift changes.

rjmccall: it was nice to shake your hand right before the coordinator person shooed us away.

Nice to meet both of you guys.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Thanks! I'll be at lunch tomorrow, for what it's worth.

neurotech
Apr 22, 2004

Deep in my dreams and I still hear her callin'
If you're alone, I'll come home.

lord funk posted:

Yeah and that shirt is great. Nice presentation!

Where can I watch this?

Kallikrates
Jul 7, 2002
Pro Lurker
https://developer.apple.com/videos/wwdc/2015/?id=106

Dessert Rose
May 17, 2004

awoken in control of a lucid deep dream...

Thanks for this, I missed it too.

edit: a great talk. super excited about basically everything in here.

Dessert Rose fucked around with this message at 10:56 on Jun 10, 2015

Doh004
Apr 22, 2007

Mmmmm Donuts...

Great job rjmccall! Super excited to get our stuff upgraded to 2.0 :D

brap
Aug 23, 2004

Grimey Drawer
Are we going to see the Cocoa APIs updated the match these new constructions soon? (i.e. methods that take an error pointer are changed to throw instead)

edit: welp, I made it to the end of the video and learned that swift automatically turns methods that receive an error pointer into methods that throw exceptions. Good poo poo rjmccall.

edit 2: One more question. Are there any plans to add something like C# async/await keywords to make asynchronous functions easier to use?

edit 3: Sorry for the many edits. I am just now expressing relief that count() will presumably become an extension method on CollectionType.

brap fucked around with this message at 01:15 on Jun 11, 2015

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"
I'm loving the new features in Swift 2 so far. Great work, rjmccall and everyone else involved :)

One thing I've been needing a lot of lately is generic typealiases, usually for giving more friendly names to blocks. I would like to write something like this (contrived example):

code:
typealias Parser<T> = String -> T

func parse(s: String, p: Parser<T>) { ... }
But I end up having to write it like this:

code:
struct ParserOf<T> {
  typealias Function = String -> T

  private init() {} // Prevent instantiation outside this compilation unit
}

func parse(s: String, p: ParserOf<T>.Function) { ... }
Is this legitimate? Using structs that I never plan to initialize for namespacing purposes feels dirty.

fleshweasel posted:

edit 2: One more question. Are there any plans to add something like C# async/await keywords to make asynchronous functions easier to use?

I would love this. Given how prevalent async blocks are nowadays, having something like this built on top of GCD would be awesome. (But I imagine there might be some desire to keep language features separate from platform APIs, at least aside from the necessary ObjC specific stuff.)

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
The struct namespacing thing seems like a reasonable workaround. Leave a comment so future readers know that's why you did it, maybe.

We definitely would like to do something to make async code easier to work with. Can't speculate about concrete plans, though.

Doctor w-rw-rw-
Jun 24, 2008
Okay, finally diving into Swift, and using an API familiar to me to do so. Dumb question time:

Swift code:
public protocol JSONParseable {
    init?(dictionary: [String: AnyObject])
}

public enum Kind : String {
    case Link = "Link"
    case Listing = "Listing"
    // ...
}

public struct Wrapper<T: JSONParseable> {
    let data: T?
    let kind : Kind?

    init?(dictionary: [String: AnyObject]) {
        guard let kindStr = dictionary["kind"] as? String else {
            return nil
        }

        // Is this right?
        kind = Kind(rawValue: kindStr)
        if kind == nil {
            return nil
        }

        guard let dataDict = dictionary["kind"] as? Dictionary<String, AnyObject> else {
            return nil
        }
        // Want to check T's type based on value of kind variable before attempting to parse. How to do this?
        data = T(dictionary: dataDict)
    }
}

public struct Link : JSONParseable {
    // ...
}
The wrapper initializer looks kind of messy.

Basically, the API returns JSON objects that envelop the data object and the data's kind (only a small handful, and known ahead of time), and I want the initializer for a Wrapper<Link> to fail if kind is not .Link, and Wrapper<Listing> to fail if kind is not .Listing, and so on. How do I need to change my approach / code to accomplish this?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Feels to me like what you want is something more like:

Swift code:
typealias JSONDictionary = [String: AnyObject]

public protocol JSONParseable {
  init?(dictionary: JSONDictionary)
  static var kindString: String { get }
}

func parse<T: JSONParseable>(dictionary: JSONDictionary) -> T? {
  guard let kindStr = dictionary["kind"] as? String,
        kindStr == T.kindString,
        let dataDict = dictionary["data"] as? JSONDictionary else {
    return nil
  }
  return T(dictionary: dataDict)
}
I'm not totally sure what you're doing with the wrapper struct, though.

Doctor w-rw-rw-
Jun 24, 2008
Any chance that we can get swift code formatting support (a la clang-format)? I like being sloppy with my spaces, and lament its loss.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

Doctor w-rw-rw- posted:

Any chance that we can get swift code formatting support (a la clang-format)? I like being sloppy with my spaces, and lament its loss.

In the meantime, there's SwiftLint, which doesn't do quite what you want but might give you some ideas if you get antsy.

Doctor w-rw-rw-
Jun 24, 2008

pokeyman posted:

In the meantime, there's SwiftLint, which doesn't do quite what you want but might give you some ideas if you get antsy.

Neat, I'll take a look soon. Currently investigating how feasible it is to adapt ComponentKit to Swift.

Definitely enjoying Swift 2.0 quite a bit after having gone down the C++ rabbit hole for a couple of months. Thanks rjmccall + eschaton!

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
The Protocol-Oriented Programming session was very interesting, and I've been playing with protocols and their extensions. Three things have popped out to me so far:

Why can I only provide default implementations for protocol methods in extensions?

I don't really understand how doing everything by protocols is really superior to having a mocking framework. It means that for every protocol, I have to hand-roll a custom object that conforms to that protocol and captures the test data I need along with setting up canned responses. Part of the point of a mock is saying "Hey, give me a thing that conforms to this API (such as specified in a protocol) and track everything that happens to it, please". What am I missing about how using protocols to mediate all of my APIs is superior to mocks?

Why can't I specify what sorts of errors my function can throw? If I look at a framework's public API, for instance, I can easily see that a given function will throw errors, but when it comes to writing my error-handling code, I have to guess at which enum it will throw. I either have to run it and print it out and see myself (and hope there isn't something I'm missing), or I have to hope that it has accurate documentation.

That said, I'm happy for a lot of the good things we're getting in Swift, and am pretty happy with the language direction. It's just easier to complain about little things than praise the grand majority of things.

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

rjmccall posted:

Basically, three reasons. (Also, in this particular case the "you" is specific: this is primarily my design.)

Code proximity is the first. In the common case where a cleanup is mandated by some specific action, you can immediately see that the cleanup is necessary instead of having to carefully analyze the rest of the control flow from that point. Now, this advantage is in inherent tension with code sequencing, because defer makes the compiler inject implicit code along exits from the scope, whereas finally appears in its proper order if you think of the jump as passing through the finally block. However, that's a bit of a leap, and I don't think it matches the way most programmers really think about it, at least not for jumps other than fall-through (e.g. return); they just think of the finally block executing, rather than thinking specifically about control flowing forward in the program. And that's fair, because finally is still pretty magical; the only code structure that's really 100% faithful to the execution flow is crappy, error-prone code duplication.

The second is that try/finally doesn't really get the scoping right. Suppose you create something, and you want to make sure it gets destroyed. In the common acquire/release pattern, there's often a variable whose scope should exactly match the lifetime of the resource. try introduces a scope, but you can't declare the variable in it, because it's out of scope in the finally and because you don't actually want to release if there was an error during acquisition. So you declare the variable outside the scope, which breaks the property we want. You could use a C#-like using feature instead, but then the cleanup is no longer ad hoc (you have to make a disposable type) and it's not always a clean fit; that doesn't mean we for-sure will never add that feature, but it's not as core as defer.

The third is that finally doesn't really compose very well. defer actions just pile up as statements in a single scope, but finally blocks have to be nested — either that, or you have to hoist variables and conditionalize the logic in the finally, and potentially even add more finally blocks there.

So why not C++ style RAII? too implicit? i can see an argument for both, with the IDisposable/RAII being what you do unless you need something special in defer.

interesting to see the rationale.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Axiem posted:

The Protocol-Oriented Programming session was very interesting, and I've been playing with protocols and their extensions. Three things have popped out to me so far:

Why can I only provide default implementations for protocol methods in extensions?

Dumb reasons. We actually didn't immediately appreciate that there was value in considering methods from protocol extensions as possible implementations. Allowing this directly in protocols is the next obvious step, and we're working on it.

Axiem posted:

Why can't I specify what sorts of errors my function can throw? If I look at a framework's public API, for instance, I can easily see that a given function will throw errors, but when it comes to writing my error-handling code, I have to guess at which enum it will throw. I either have to run it and print it out and see myself (and hope there isn't something I'm missing), or I have to hope that it has accurate documentation.

Are you actually trying to react individually to every possible error that a function can throw? And if so, why? Generally in error-handling you at most want to call out a couple of special cases — which documentation is fine for — and then treat everything else uniformly. Trying to formalize exact error lists is really, really brittle from an API design perspective, causes a lot of downstream damage with wrapping errors, and comes with a ton of complexity and pedantry as a language feature.

Malcolm XML posted:

So why not C++ style RAII? too implicit? i can see an argument for both, with the IDisposable/RAII being what you do unless you need something special in defer.

Destructors make a lot of sense as a mechanism for type-directed value cleanup, but of course you need full rule-of-five support for that, which is not a priority to provide in Swift. Destructors are a pretty crappy way to do ad hoc cleanups, which is what defer is designed for.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
Does "not a priority" mean that it's something you think may get added at some point in the distant future, or is it something you don't expect to ever happen?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Plorkyeran posted:

Does "not a priority" mean that it's something you think may get added at some point in the distant future, or is it something you don't expect to ever happen?

The former. Maybe not that distant. Also, we'll probably import C++ value types at some point.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

rjmccall posted:

Dumb reasons. We actually didn't immediately appreciate that there was value in considering methods from protocol extensions as possible implementations. Allowing this directly in protocols is the next obvious step, and we're working on it.

Cool.

quote:

Are you actually trying to react individually to every possible error that a function can throw? And if so, why? Generally in error-handling you at most want to call out a couple of special cases — which documentation is fine for — and then treat everything else uniformly. Trying to formalize exact error lists is really, really brittle from an API design perspective, causes a lot of downstream damage with wrapping errors, and comes with a ton of complexity and pedantry as a language feature.

Thinking about it, this does make sense. I was thinking more on the enum-level, but I guess my Stockholm Syndrome from Java is infecting me. I still don't necessarily trust the documentation, but I can't think of any really compelling example where I want to specially handle a particular error where I'm not going to know that such an error exists prior. At this point, my only possibly argument is "type safety!", but I don't think that's compelling enough.

So, cool.



I'm kind of curious what effect open-sourcing Swift will have on the annual cycle of big updates at WWDC. I suppose we'll find out (and at this point, the big updates might be done with).

lord funk
Feb 16, 2004

I've also been playing with protocol extensions since the WWDC talk. I can get a heterogeneous array of protocol adhering objects:

code:
var array: [MyProtocol] = [ ... ]
But I can't get a set to work:

code:
var set: Set<MyProtocol> = [ ... ]

error: Type 'MyProtocol' does not conform to protocol 'Hashable'
Is this solvable with a protocol extension?

brap
Aug 23, 2004

Grimey Drawer
Your protocol needs to implement Hashable to be in a Set because a Set needs hashing to provide O(1) lookup. Right?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
You're going to be stuck at some point because you also need an equality relation to be in a Set, and now you have the binary method problem.

You could use a struct type that wraps the protocol type, and define equality with dynamic type checks, but that'll be somewhat less convenient to use. Protocol types are inherently limited in some pretty important ways.

lord funk
Feb 16, 2004

But wasn't that what Protocol-Oriented Programming in Swift was all about? Solving the Equatable issue by using protocol extensions so we can move forward with this kind of use?

Meat Street
Oct 17, 2004

knowin' nothin' in life but to be legit
Possibly-stupid question for rjmccall or anyone else: Why does ArraySlice exist? A friend and I were trying to reason about it, but given the copy-on-write Array semantics we couldn't come to a conclusion. I'm probably missing something obvious.

Adbot
ADBOT LOVES YOU

sarehu
Apr 20, 2007

(call/cc call/cc)
If an Array itself could refer to slices of another array, you'd waste large amounts of memory when you keep around small slices of large arrays. This was a problem with Java strings. Having a separate type means that you can slice arrays but avoid the mistake of keeping around long-term copies of giant arrays.

  • Locked thread