Swift

Jun. 2nd, 2014 02:46 pm
graydon2: (Default)
[personal profile] graydon2
Today Apple introduced a new language called Swift, along with some pretty hot livecoding environment which, while neat, is an area I'm not very expert in so I'm going to ignore. I am certain the livecoding nerds will discuss it elsewhere.



Swift-the-language I've just finished (quickly and lightly) reading the manual for, so I'll suggest you take my commentary with a grain of salt, but I can point out some things that might be of interest to rushed readers who don't see the obvious bits quite so obviously as someone recently working in this space. Several other Rust nerds are also busy examining and commenting.

Logistics



  • It is LLVM-based and compiles to native code like many recent languages. It will probably run quite fast and target many devices natively, including client-side where memory use and latency kinda still matters.

  • It appears to have "Objective C object model interop" as a hard design constraint, which limits some of the stuff they can safely do.

  • It appears to be proprietary, though I hope for its sake this is not true!

  • It seems to borrow quite extensively from C# and ... I don't know how to say this politely or modestly ... Rust. Which is flattering if true! Of course I'm biased. Also a language pluralist, and since Rust is a major cobbling-together of things we liked in other languages (ML, C++, C#, Lisp, Ruby, etc.) I don't mean this to be claiming any sort of originality. It's nice to see ideas we liked and were trying to promote spreading elsewhere. I'd love to see some posts from the design team talking about their process, what they liked and disliked in other languages, tried to borrow or adapt or reinvent themselves. Convergent evolution happens a lot in languages (often to funny effect).



Features



  • No explicit pointers (but see below re: inout), it relies on the value/reference type dichotomy like C#. This is a half-way step to regaining some of the performance you lose moving to an all-heap system like Java (note: Java is toying with value types now too) without having the cognitive and design load of explicit pointers. This looks like its biggest difference from Rust. It also means that you can't easily (say) take a pointer to the middle of another structure or array. Which is sad, but a reasonable compromise.

  • Obviously in keeping with that, no real mechanisms for reasoning about ownership. When you have a reference type it's an ARC pointer into a shared heap (or its weak partner), period. Regions, uniqueness and all the associated performance and also cognitive load from Rust is not present.

  • Single inheritance classes with explicit overriding and properties, plus multiple inheritance interfaces ("protocols"). This is the norm these days, very C#-ish again, thought the protocols do not appear to have default methods they can carry with them. Classes are reference types, unlike structs which are value types.

  • Protocols get to play double duty as either concrete types (in which case they denote a reference type you can acquire with as from any supporting type) and as type constraints on type parameters in generic code. This is a delightful convenience Rust stumbled into when designing its trait system and I'm glad to see other languages picking it up. I'm sure it has precedent elsewhere.

  • Post-hoc adaptation of types to unrelated protocols using extension. I'm not sure what the coherence rules are on this, I can't find them.

  • Lambdas appear to use a very similar Ruby-ish block form (right down to the trailing argument form!) that Rust used a year or two ago but has now moved away from (I kinda liked it). Also a cute even-shorter form using numbered variables, like the Clojure #() reader macro.

  • Nice normal functional-language-y algebraic types with tuples and sum types, the latter introduced with enum as in Rust. Pattern matching and destructuring binding as you'd expect, nearly identical to Rust except obviously without the wrinkle of trying to bind explicit pointers. Also they avoided all the syntactic ambiguities that wound up in Rust's pattern language, much to my dismay (they get disambiguated during name resolution later).

  • Local type inference, tidied up numeric types, nicer literals, no implicit coercions. Yay.

  • Script-language-y (and Go-ish) support for dictionary literals. Minor, but will be popular.

  • A basic module system without globs, grouped imports or renaming. No visibility control. Re-exporting is supported though, through attributes.

  • No macro system as far as I can tell.

  • Non-pervasive-NULL, thank goodness. Instead, a copy of the option-type / nullable sugar that shows up in C# and the recent Facebook "Hack" language, including quite a bit of sugar for option chaining, forcing and binding. This is great.

  • let and var to differentiate immutable from mutable bindings.



Peculiarities



  • Arrays have strange copy-on-extension semantics, but I think are always in the heap, maybe? Strings are also seemingly CoW and heap. I think. It's unclear.

  • Unclear how or if you're allowed to implement the iterator protocol yourself.

  • Visually confusable .. and ... operators for exclusive and inclusive ranges.

  • Parameters can be inout and such arguments require the seemingly invincible unary& operator! Funny, the inout keyword (also in Objc) refuses to die! In Rust, before we grew a first class region pointer ("lifetime") system, we did this sort of thing also ("parameter modes"). The asymmetry between argument-passing modes and "real" pointers eventually became too much to bear, but I guess Swift is betting it won't be so bad.

  • Seemingly not an expression language. I guess "no macro system" so...

  • No discussion of error handling aside from the algebraic types, option types, and mysterious but occasional reference to "runtime error". Not sure what the isolation / recovery system is, or if there is one. The word "unwind" doesn't occur in the manual.

  • An approach to named parameters that looks suspiciously like the "Olabl" variant of Ocaml. I am not sure how much use this is likely to get!

  • Checked arithmetic by default, and hex floating point literals. Yay!



Overall I mostly agree with Bryan O'Sullivan's tweet:



When I started working on Rust, a lot of the motivation was the pain of losing all the nice ML-isms I enjoyed in my hobby hacking when it came time to work in C++ "for pay". We actually tried to pull a lot of this stuff (algebraic types, pattern matching, options, annotation-light typing) into the failed ES4 project as well, but it sank under its weight.

It's remarkable to me that in the years between, we've seen such a shift in what's considered "normal" new-language tech. F# is shipping on several platforms (whether or not M# ever actually surfaces again); Scala is considered an employable skill; C++11 has lambdas and local type inference at least, if not algebraic types or pattern matching; Rust actually exists now; and now one can rely on similar comforts in the Apple ecosystem. How delightful!

confirmed

Date: 2014-06-03 10:27 pm (UTC)
From: [personal profile] zooko
Here's apparently the primary author of Swift, naming Objective-C and Rust first, in his list of languages that informed Swift:

http://nondot.org/sabre/

HT @BrendanEich (https://twitter.com/BrendanEich/status/473943219487514625)

Re: confirmed

Date: 2015-06-30 01:16 am (UTC)
franklinchen: Casual photo of myself (Default)
From: [personal profile] franklinchen
A year later, Swift continues to improve, and most importantly, Apple is going to open source it. It's good times for those who waited 20-30 years to move beyond C/C++/Java! It's particularly good that the designers of the new languages care about usability up front.

Date: 2014-06-10 06:29 am (UTC)
From: [identity profile] rjmccall.livejournal.com
If there's a common Swift-ish design aesthetic, it's to provide a basic language tool and then try to make its ecosystem great. So, for example, we have no equivalent to Rust's amazing borrowed reference types, but we do have raw pointers (UnsafePointer), which are, as you might expect, totally unrestricted and unsafe. You can't normally make a raw pointer out of a variable/property, but you can as a special case of passing an argument, which naturally scopes the raw pointer; this rule also makes C interoperation much less painful. Of course, the callee can let the pointer escape, but that's why we named it UnsafePointer.

The double-duty of protocols really is quite nice, but to be honest, it would have been hard to avoid it! We already had protocols-as-existentials in Objective-C, where it's an increasingly common design pattern; and of course we were familiar with protocols-as-constraints from SML signatures, Haskell type classes, C++ concepts, and any number of other languages. It would take a lot of stubbornness to maintain two different language features for all that.

A number of your complaints are things we're hoping to address later, for various definitions of "later" — but unfortunately, I think being any more specific than that would run afoul of our marching orders not to publicly speculate. I will say that a lot of us individually are quite committed to open source!

Date: 2014-06-11 09:20 pm (UTC)
From: [personal profile] rjmccall
I seem to use my livejournal primarily to comment on things primarily posted on dreamwidth these days, so I suppose I might as well sign up here.

Swift can run generic code generically, using what's essentially a vtable for value semantics. Among other things, this means that we don't have to semantically restrict generics to only places which we can statically specialize: for example, we can dynamically dispatch generic functions and hence use them as class methods, protocol requirements, and even in principle as first-class function values. We're still shaking some bugs out with nested generics, so some of this is restricted, and of course higher-rank polymorphism adds a ton of type-checking complexity that we have chosen not to pursue yet; but the runtime model supports it.

This value-semantics vtable (we call it a "value witness table", and all of our type objects have a pointer to one) contains functions for copying, moving, and destroying values as you might expect; it also contains functions for storing the value in a fixed-size buffer, essentially a void*[3]. Those latter functions are useful for storing values of the type on the stack in generic code, but they're also useful for existentials. Our existential representation is essentially:

  struct ExistentialPrintable {
    FixedSizeBuffer buffer;
    struct SwiftType *storedType;
    // Any protocol vtables required by the existential follow:
    struct PrintableProtocolImplementation *printable;
  }


In other words, a sort of typically-inline boxing.

As I'm sure you can imagine, this all means that generic code is not exactly fast, so our optimizer has the ability to specialize generic algorithms for specific arguments. In the long term, we think this will give us a lot of flexibility to avoid code bloat around redundant specializations, e.g. by forming partial specializations for types with identical representations, or by choosing not to specialize functions which don't show up in profiles. Right now, the optimizer is just doing aggressive deep clones, so I expect we have the same code size problems that Rust does.

The performance problems mostly all boil down to our optimizer still being very immature, mostly for terrible and kindof embarrassing product-development reasons.
Edited Date: 2014-06-11 09:20 pm (UTC)

Date: 2014-06-17 11:51 pm (UTC)
From: [personal profile] rjmccall
Heh. Yeah, with our optimizer in its infancy, it is still possible that we'll have to retreat to specializing eagerly. We'll see, I guess.

Rust credit given.

Date: 2014-06-03 10:20 pm (UTC)
From: [identity profile] jeoff wilks (from livejournal.com)
Second in Chris Lattner's credit list is not bad. "Of course, it also greatly benefited from the experiences hard-won by many other languages in the field, drawing ideas from Objective-C, Rust, Haskell, Ruby, Python, C#, CLU, and far too many others to list."
-- http://nondot.org/sabre/ (Chris Lattner)

confirmed

Date: 2014-06-03 10:23 pm (UTC)
From: [identity profile] zooko (from livejournal.com)
Here's apparently the primary author of Swift, naming Objective-C and Rust first, in his list of languages that informed Swift:

http://nondot.org/sabre/

(I tried to post this on dreamwidth.org following your request, but it said "Only friends of graydon2 may post in this journal.".)

Date: 2014-06-26 01:01 am (UTC)
From: [identity profile] kinra.livejournal.com
It makes me really happy to see you so positive about the state of computer languages right now, Graydon. Smile to my face, even though I know this post was weeks ago.

January 2025

S M T W T F S
   1 234
567891011
12131415161718
19202122232425
262728293031 

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 3rd, 2025 07:47 am
Powered by Dreamwidth Studios