Hacker News new | past | comments | ask | show | jobs | submit login
Interesting Swift Features (mikeash.com)
94 points by ingve on June 20, 2014 | hide | past | favorite | 74 comments



Explicit optionals are great. But I feel some of the other convenience features like type inference, trailing closures, different copy behavior of arrays, and operator overloading have the potential of being abused to produce cryptic code. What's your opinion?


Maybe I'm just lucky, but I've never seen operator overloading abused, and I've written my fair share of C++. Bad developers usually don't learn about these things in the first place, they just spew mountains of poorly-abstracted code.

Ruby lets you do far worse things if you dig deep enough, but nobody ever complains about that.


I've always thought of the stdlib overloading of << in streams to be, well, maybe not an abuse on the scale you see in the obfuscated C contests but at least a place where operator overloading makes code harder to read and understand.


Honestly, I can see why C++ overrode it in the first place though - does anybody really give a crap about the "<<" operator? Why does a language need to dedicate a whole operator to bitshifting?

I assume Stroustrup et al took a look at the meaningless "<<" and decided it was available for whatever use they saw fit.


Slight counter point re: ruby, I complain A LOT about ruby's and the communities tendency to monkey patch everything. It's a dangerous practice that needs to be REALLY well thought out. just like C++ and the operator overloading, I've had bad and good experiences with both. I don't think they need to be removed from the languages but I do think good experienced developers should loudly and quite often proclaim the dangers of over use.


I think it comes down to culture, not language features.

Objective-C allows much the same monkey-patching craziness as Ruby, but the feature is almost always used to just add additional convenience methods to system classes in a fairly safe and sane manner.

You can do much worse (and I certainly have), but it's pretty rare.


Same and possibly more so in Python. Monkey patching is regarded as a last resort in clever libraries that need to do things that would be impossible any other way. They are generally clearly signposted as 'smelly but unavoidable'.


I agree, I'm a python/django developer in my day job and a ObjC/iOS at home. Both are just a easy to monkey patch as ruby but it's much less used. It's one of the reasons I prefer python to ruby.


I'm with you there. I hate when anything extends built-in types. Since all code uses built-in types, you can quickly end up in debugging hell.


> Ruby lets you do far worse things if you dig deep enough, but nobody ever complains about that.

Well, people _used_ to, but we developed cultural tendencies to reduce those kinds of issues. The last time I was bit by a bug like this was years ago.


This fear should be reserved for language features which effectively require cryptic code to be useful. For example, Perl's large family of built-in variables with bizarre symbols for names, or the proliferation of redundant and nested template types for things like iterators in C++ before C++11 added type inference.

For features which just allow cryptic code, it's basically the fear that you won't be able to resist it, or that other people won't. People who want to write cryptic code will figure out how to do it no matter what language they use and what features it has. The solution is to either educate them or not work with their code, not try to restrict languages in a vain attempt to make it impossible to write bad code in them.


>>For example, Perl's large family of built-in variables with bizarre symbols for names

There has been "use English;" since at least Perl 5.8, which is about a decade old iirc.


Appreciate the info. Consider my example to apply to earlier versions, in that case.


Trailing closures turn out to be magic when creating APIs and mini-DSLs. That slight reduction of friction that unifies regular language syntax with user-defined functions is often the difference between a high usable extension and a kludge.


Do you happen to have any good examples? They look like a great feature but I'm less familiar with how they've already been used in other languages.


Scala is full of many libraries that take advantage of trailing closures. I think their standard library Future/Promise API is good example: http://docs.scala-lang.org/overviews/core/futures.html

Edit: Another popular one is the ScalaTest unit testing framework: http://www.scalatest.org


Take a look at one of the tutorials on writing a Ruby DSL. E.g. http://rubylearning.com/blog/2010/11/30/how-do-i-build-dsls-...


I think Ruby is the major banner-carrier for this feature. It is actually more awkward in Ruby to have non-trailing closures.


The latter two, yes. Operator overloading is a loaded gun and you need to make sure you aim carefully. And Array identity and mutability semantics are just straight-up broken. But type inference tends to be good for readability in my experience, and trailing closures seem clear enough that I don't see how they have any more potential for abuse than anything else.


Operator overloading can potentially be abused because in Swift you are not limited to standard operators like in C++.

https://developer.apple.com/library/prerelease/ios/documenta...

According to the documentation you can declare an operator composed by an arbitrary list of this characters "/ = - + * % < > ! & | ^ . ~". You can recreate C++ stream operators (<<, >>), Ruby combined comparison (<=>), and so on.


I think the danger of abuse is stronger when you overload existing operators than when you make up new ones. The latter can't fool you on its semantic and behavior.


I bet we will see some interesting examples of fooling in obfuscated Swift contests.

For example one can define operators called <>, =<, =>, and !=

You can do interesting stuff with pre- and suffix operators, too. For example, the XML comments <!-- and --> could be a prefix- and suffix operator pair.

Also, you could define operators that look conspicuously like comments, at least to some of your readers.

[disclaimer: I don't have access to the code; I just read the documentation. That documentation seems to imply that // starts a comment _and_ can be used to implement a custom operator. Even if that isn't possible (fairly likely, I would say, you can still try using --- or /// as your operator]


Ok, let me replace my “can’t” with “is a lot less likely to”.

I checked: neither // nor /// can be used as operators (syntax error: they both just start a comment). All your other examples work, but I would say those mean the author is intentionally trying to trick me. Or he’s writing a clever DSL, but that line is very fine and treacherous.


Thanks for the checking. And I agree: that line is thin.

Some other food for thought:

  a = b-- // !z
  x = y--// ! z
The first does

  a = b - 1
and has a comment, the second calls suffix --// on y, then infix ! On the result and z.

A variation:

  x+ - 3
  x + -3
I think it is fair to say that Swift introduces a new variant of whitespace sensitivity.

Also, the docs do not seem to discuss line breaks inside statements. If they are legal, you can do:

  x+
  -3
vs

  x+{space}
  - 3
I think I would find it fun to stress test this language.


I don't know whether this is reassuring or worrying, but Haskell has long (always?) pursued this strategy of allowing even more flexible operator names. From http://www.haskell.org/onlinereport/lexemes.html:

> Operator symbols are formed from one or more symbol characters, as defined above …

where 'above' is:

    symbol 	-> 	ascSymbol | uniSymbol<special | _ | : | " | '>
    ascSymbol 	-> 	! | # | $ | % | & | * | + | . | / | < | = | > | ? | @
	| 	\ | ^ | | | - | ~
    uniSymbol 	-> 	any Unicode symbol or punctuation


They go so far that the operator name can even be prefixed by the lexeme (?) for single-line comments (--) :)

> (--|) = (+)


My problem with most overloaded operators is that they too often have no sensible name.

When I talk to myself while coding, I have no name to say out loud.

I have nothing but a picture, when I try to remember the operator.


While the names may or may not work for you, this has been considered:

http://stackoverflow.com/questions/7746894/are-there-pronoun...

http://stackoverflow.com/questions/3242361/haskell-how-is-pr...

I got to this by Googling for the 'fish' operator, to which I vaguely remembered seeing a reference:

http://www.reddit.com/r/haskell/comments/c262b/the_fish_oper...


I'm quite fond of being able to define new operators. Particularly in functional languages, it allows you to effectively extend the language. For example, F#'s designers didn't include the usual monad operators. It's easy enough to define your own >>=, though, so life is still good. Similarly, ML developers can create their own forward application ( |> ) operator to also get good access to a useful feature that isn't included by default.

In languages that don't allow you to overload operators at all, the alternative is to use explicit function syntax. That can sometimes be a short path unreadable to code that's littered with pyramids of doom.

In languages that only allow you to overload existing operators, you end up with monstrosities like C++'s << and >> operators, which overload the bit-shift operators with new and completely unrelated semantics. It's that kind of shenanigans that give operator overloading such a bad name.


> Operator overloading can potentially be abused because in Swift you are not limited to standard operators like in C++.

It is both a blessing and a curse, as far as potential for abuse is concerned.

You pointed out the downside of arbitrary operator names. The downside of operator overloading on fixed operator names is that you are limited in your choice of operators, so instead of choosing a more distinct operator, you sometimes have to go with things that are kind-of-the-same, or maybe not close at all. And instead you also have the problem of misleading people, for example by using the +-operator for set union, while the reader thinks you are adding together numbers.


Any language feature has the potential to be abused to create cryptic code.


Sure, the effect of operator overloading is no different than that of using undescriptive names. Except with operator overloading, the name is almost guaranteed to be undescriptive.


I must disagree strongly. With operator overloading, the name is guaranteed to be highly descriptive. Think about all that's implied by something as simple as + or /. They might seem superficially undescriptive, but they carry a lot of context due to the huge amounts of history these symbols have.

The problem with operator overloading isn't a lack of descriptiveness, but using them for things they don't describe. If you overload / to perform network IO or something, the problem isn't that / is an undescriptive name, the problem is that it means "numeric division". It's no different than if you wrote a function called "divide" that did network IO.


'+' or '-' doesn't adequately describe operations even in the numeric domain (e.g. overflow conditions). They are recognizable only because almost every language has similar semantics for integer or floating point arithmetic. But even using '+' for something like string concatenation unmoors it from historical context.

And beyond that, you're just making things up. '<<' for input/output?

We spent thousands of years developing the written word just so we wouldn't have to rely on primitive pictographs to vaguely get our points across.


That "only because" is my entire point. It's like, aside from the whole reason symbols are useful, they're not useful.

If you've been paying attention, I hold up << for IO as an excellent example of operator overloading gone wrong. But it's not because << is undescriptive, it's because << means "bit shift".


Your justification of "huge amounts of history" only applies in very limited contexts, all of which can be hard wired without allowing general operator overloading.

"<<" does not mean "bit shit." Several languages use it to mean "bit shift," but many do not, and even in those languages the usage is ambiguous. C uses two operators that, visually, could refer to six different operations: arithmetic shift left/right, logical shift left/right, rotate left/right (on x86: shr, shl, sar, sal = shl ror, rol). As a result, the C standard leaves right-shifts of signed negative numbers implementation defined.


How can those limited contexts be hardwired without allowing general overloading? What if I want to write a new numeric type?

Seems like you're just talking about operators, not overloading. If << doesn't have a distinct meaning then it shouldn't be used. If it's OK to use << it should be OK to use it both for built-in types and custom types.


I'd argue that << was a bit of a misfeature anyways. Providing operators for mathematical symbols with well-known meaning is sensible. == is an understandable hack, but a hack, nonetheless. || and && probably could have just used the words AND and OR like SQL does.

The << operator was just using symbols for the sake of symbols and would have been much more semantically clear with Shift(value, nBits) and would've avoided temptation to overload this meaningless operation.


You have a good point. I don't know if I completely agree, but I can see the merit in it. However, you're now talking about the merits of operators in general, rather than overloading, which is a different thing altogether.


The issue is that operators rarely have obvious semantics when applied to non-builtin types. '+' makes sense for integers and floating point numbers, complex numbers and ratios, and almost nothing else. Those types can be built into the language, and '+' defined on them. It doesn't make any sense for strings and all the mismash of things people will overload it for.


I've never seen anybody confused by "+" for string-concatenation. It makes far more sense than, say, integer division where 5/3 = 1.

Just because C++ ruined operator overriding with their moronic << doesn't mean that every other language should live without perfectly reasonable operator behavior. I've been dealing with Java instead of C# and not being able to use simple obvious equality checks with the "==" operator is agonizing.


Languages should build in all the numeric types you could ever need, but they don't. A lot of languages still don't have arbitrary-size integers (Swift among them).

You see the vast majority of cases not needing operator overloading and see it as useless, while I see the small number of cases where it's really, really good and see it as important.


To reductio ad absurdum, we shouldn't even include arithmetic operators for numerical types because of ambiguities regarding truncating decimal places. After all, myInt1 / myInt2 is dangerously undescriptive so really all integer division should use

Math.DivideToFloat(myInt1, myInt1) or Math.DivideToInteger(myInt1, myInt2)


Language design has to balance expressiveness with maintainability, and there is absolutely no question that some additions lean far more to the former than the latter.

Operator overloading, as mentioned, is something that seems fantastic when you're banging out a bunch of code. When you return to that code a month later, however, with no context, it leads to mystery code with completely undefined behavior without tracing back through every constituent. We constantly see people make the (unsupported) claim that scientific coding simply needs operator overloading, and while I can't speak specifically to that industry, in the financial industry operator overloading is how you end up with terrible, mystery-meat code.


I think a language should either have operators, or it should not.

There's a decent case for not having operators at all. Lisp being a fine example of this. Everything is a function, end of story.

But once you have operators, what's the rationale for restricting them to built-in types? This, to me, fundamentally makes no more sense than, say, banning user-created types altogether.

Clearly nothing needs operator overloading, but if you have operators at all, why should "a + b" be valid if and only if a and b are certain language primitives? If you think + should only be used for numeric addition, I'd totally agree, but numeric types don't have to be restricted to built-in ones.


I can certainly understand how that sentiment arose back in the dark ages of early operator-overloading abuse.

I'm starting to wonder, however, whether the horror stories are continuing to propagate long after anyone has seen a real, living monster.

If I'm browsing someone else's code today in a modern development environment and I encounter a function I don't know it's generally pretty easy to navigate to the definition (if it's in the codebase) or documentation (if it's not).

If operator overloading is just a function with a name that happens to be a string of symbols and with infix application at the call site, can't I find out about it just as easily?

What's the difference between

   myMysteryFunction(a,b)
and

   a + b
…if I know that the type of a and b isn't something ordinary like an int or a float?


Well, for me, the latter would masquerade as a trivial expression seen thousands of times, and which now may or may not have unexpected behavior. Every instance of something like a + b must now carry the slight extra cognitive load of potentially having a sort of "optional type annotation" in one's mind. Usually normal, but might not be.

The former is at least potentially self-documenting. And even if it's a badly named function, at least you know it's a special function, and you know you'll have to go look up its behavior.

[Edit: Clarity]


Done right, it should masquerade as a trivial expression seen thousands of times. There's a real advantage in being able to have user-defined types that have the same interface as native types.

I want the ability to do 'a == b' regardless of whether it's a built-in or user-defined type. That's abstraction.


That's a good point, and I agree. The situation I'm thinking of is something like, say, you have an object or type called a "Tire". It has width, diameter, weight, price, tread depth, compound, etc. As you work with Tires in your program, you frequently end up having to compare their widths. So you overload the ">" operator to return true if one Tire has greater width than another. Your code ends up festooned with these comparisons.

Flash forward one year, you're long gone, and new developers on your team are left wondering which aspect of a Tire is used in comparisons with ">". Everything works, but it's easy to see how the ambiguity could lead to subtle submarine bugs; incorrect developer assumptions about the behavior of ">" may produce mostly -- coincidentally -- working code.


I think I'd rather take that risk than have another scenario like BigInteger in Java:

http://stackoverflow.com/questions/1783912/java-how-to-use-b...


It's true that I can't provide evidence that scientific computing NEEDS operating overloading beyond my own anecdotal experience. However, I've also never seen any support that operator overloading muddies code, besides other people's anecdotal experience.

The simple example I always come back to is how hard it is to find the bug in the following code:

    divide(add(multiply(-1,b),multiply(-1,sqrt(add(multiply(b,b),multiply(multiply(-3,a),c))))),multiply(2,a))
versus finding the same bug operator overloaded version

    (-b-sqrt(b^2-3*a*c))/(2*a)


That's a good point, but those operators have really clearly-defined semantics, so they don't have the ambiguity issue.


I'll also concede that I've seen some try abominations with oeprator overloading. Would people be willing to compromise with just allowing the overloading of addition, subtraction, multiplication, and division?

I know that overloading << or || can lead to some confusing code, but offering the basic arithmetic operators would end 90% of the whining by the people who do scientific code and need this functionality.


Do you prefer the C library interface for working on strings to the way that Java handles them, then?

After all, Java allows statements like "string3 = string1 + string2;", which is much more ambiguous than C's "strcpy( string3, string1 ); strcat( string3, string2 );" (or a modern equivalent where those functions are namespaced in a string class).

I work on 3D games. Sometimes I work in languages (c++, c#, shader languages) that allow me to use operator overloading (and thus infix notation) for 3D vectors and matrices. And sometimes I work in languages (actionscript, java, javascript) that don't allow me to use operator overloading and infix notation for vector and matrix math.

The code in the latter set of languages is much, much, much less readable by nearly any reasonable measure.


Let me expand on what I think is the point of the GP:

I hate operator overloading myself, but at the same time I'm also certain there are use cases where it is a big win. (I assume it is in Swift because it could be motivated well.)

You'll have to specify coding standards anyway -- and make certain that they are followed. Just add a paragraph about having to get a senior developer's signature on any use of operator overloading, on pain of needing to update the CV.


Operator overloading is really, really good if you're writing numerical code. It's sad that people abused it enough to give it a bad reputation.


Of course they do. But as long as the standard libraries restrain them selves, the culture of the language shouldn't go overboard (I hope).


How would you even be able to abuse inference? If someone leaves some types to be inferred and someone reading that code is confused, the compiler already knows the type of the function so it should be able to spit it out for him in something like a REPL or even to 'inline' it in the source code.

Maybe you mean that people should write some types as some kind of assertion in case the code changes (and it compiles again, coincidentally because the change produced valid but different types, somehow)? I guess the same could be said for always putting certain assertions in the code in order to make sure that assumptions are sound, for example putting "@Override" on all methods that are intended to be overrided methods in Java. And again, since types are inferred, I guess it is conceivable that it can be automated like a code-style thing as a hook, since the compiler knows the type.


I've known some developers who were uncomfortable with abstraction in general, and like to have everything be as explicit as possible. So when reading code along the lines of:

    var employee = employeeRepository.Get(12345);
    return employee.Name;
they'll see the code as untrustworthy because without an explicit type name, you've got no idea what kind of thing 'employee' is.

I suppose there's also the 'making excessive generics too easy' issue, though personally I'm not certain that excessive generics are necessarily so terrible. Oftentimes the alternative is to litter your codebase with a bunch of one-off classes that bloat the codebase without adding any value. I think I side with the camp that cites that practice as one of the things that used to really suck about working in Java.


Here's what I had in mind. Let's say class S is subclass of class C. Now,

var myVariable = S()

when changed to

var myVariable = C()

will not cause any compiler error in rest of the code. Still, it may be a logical requirement for myVariable to hold instances of type S only. Yes, the programmer can explicitly declare type if s/he wants to, but I guess apple APIs will promote the culture of using type inference. Though hopefully apple will set the best tone possible of swift coding style in their APIs.


I guess inference can be used to mask some smelliness of deeply nested generics. It seems FUD-like to raise these types of concerns solely in the abstract, though.



Thanks for posting that. I don't know if it was the HN Hug of Death or a coincidence or what, but the VPS spiked to 100% CPU and became completely unresponsive, even in the console accessed through the host's web panel. A reboot has fixed it... for now.


It's back up now


Don't know whether it's just because a lot of the library code hasn't been fully moved to this model yet, but I'm still finding most (possibly all) of the iOS API code to use the same error:&error) approach that Objective C uses.


Approximately none of the iOS APIs have been adjusted for Swift yet.

Also, at least for Swift 1.0, they won't be. Swift does not guarantee a stable ABI right now or for 1.0. They guarantee binary compatibility with the OS, and ABI compatibility with any application frameworks that are compiled alongside the application (because, well, those can't change), but no compatibility between frameworks and applications. The only way to get that compatibility is to stick to Obj-C APIs (or, well, pure C APIs, but that's no better).

What this means is system frameworks will not be able to take advantage of things like generics or multiple return types until sometime in the future, past Swift 1.0. Of course, they couldn't justify doing that today anyway, because they need to maintain Obj-C compatibility for all of the Obj-C code that still exists.


I'm guessing you're right. Their veil of internal secrecy wasn't lifted until very recently, and their design does signal a longer-term transition is in play.

The real work of modernizing the libraries probably began only after WWDC.

There was a job listing posted to llvm-dev after the keynote that gives a few clues.

http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-June/073669....


That's because all of the existing frameworks are written in Objective-C. Rewriting the frameworks in Swift is probably an even bigger undertaking than the Swift language itself (and one can't just change the frameworks to fit Swift's idioms for compatibility reasons).


It's a small thing but I think let/var deserved a mention too (my C code is littered with const everywhere).


He did say he was going to write a series of articles about Swift. Give him time. :-)

The main "aha" point of this article for me was the way optional values eliminate a whole class of programming errors that are pervasive in the "C family" of languages, and does so in a rather elegant way. (Apparently Scala has a similar capability, but I'm not familiar with Scala.)


Scala, Ocaml, SML, Haskell, Swift, C# (the Nullable type) and many other languages have Optionals/Maybes/Nullable types (all names for the same thing).

I agree Optional's should booster safety (in the iOS/OS X world) if Swift can convince everyone to start using them.


People are probably most familiar with them, I think, in the form of Haskell's Maybe monad. (Though that has the extra feature of black-holing computations that try to operate on Nothing.)


Swift has the "black-holing" as well in the form of either explicitly calling Optional's map method, or the syntactic sugar: myTypeTOpt?.methodOnTypeT()


> People are probably most familiar with them, I think, in the form of Haskell's Maybe monad.

Or just simply "Maybe" ;)

https://groups.google.com/forum/#!topic/elm-discuss/1acyOfxv...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: