Wednesday, June 25, 2014

A Couple of Words about Swift

It seems everyone has something to say about Swift, the new language for developing on Apple’s Mac and iOS platforms.

Things like: “Do we really need another programming language?” Or, “Clearly, Swift borrowed feature X from language Y.” Or, “Boy, not including feature Z really cripples Swift.”

Am I any more qualified than the rest of the rabble to comment? No. But I won’t let that stop me.

Swift is an Amazing Achievement

Consider Objective-C, the language in which the Cocoa frameworks are written.  At birth, Objective-C most closely resembled Smalltalk-80 glued to a C compiler.  The syntax for message sends was clearly borrowed from Smalltalk, and the extreme late-binding dynamic behavior was, too.  Over time, Objective-C acquired a little bit of static typing, but it was mostly hints, not guarantees that a compiler could (or should) rely on.  At bottom, every message send went through dynamic dispatch (objc_msgSend).

Objective-C, some joked, combined the type-safety of C with the performance of Smalltalk.

Objective-C was the big loser in the battle for a mainstream language that extended C with object-oriented capabilities.  (C++ won that battle, but it ultimately lost the war to Java.)  Developers knew that Objective-C was an Apple-only ghetto.  But they also knew that the language was not as important as the frameworks.  Remember, all of NextStep Cocoa was written in Objective-C.  So, they put up with the limitations of Objective-C, but something was happening in the world.

After a (Java-induced?) lull, people started paying more attention to programming language alternatives.  C++ templates and Java generics made strict compile-time typing the norm, and programmers began to get used to the idea of having the compiler eliminate a large class of errors.  Compile-time type guarantees also eliminate most run-time checks, yielding performance improvements.  Type inferencing started making inroads into mainstream languages, so that typing declarations didn't require so much typing on the keyboard.  Lambda expressions, first-class functions, and closures also started to make their way into the mainstream from the functional programming community (one area where Objective-C’s blocks were slightly ahead of the game).

The end result of this was, I think, a renewed interest in languages that combine expressiveness with static type safety.  And Apple was at risk of having developers feel a pull away from the Cocoa platform because the Objective-C language was starting to seem behind the times.  Objective-C was not as easy or as fun to use as the newer alternatives.

A New Language, or a New Language?

Apple’s challenge, then, was to introduce a new language that could be used with the existing Cocoa frameworks.  And that, I claim, is exactly why no existing language—no matter how wonderful—could fill the need.

Watch the WWDC 2014 presentations on Integrating Swift with Objective-C and Swift Interoperability in Depth.  Now ask yourself, for every existing candidate language, could it have been adapted to interoperate with Objective-C the way that Swift does?  No.  Could any of them have been tweaked a bit?  Maybe, but at what cost?  Don't forget that many of these languages are relying on an underlying virtual machine with a very specific memory model, and once that is changed, much of the existing implementation cannot be reused.

The constraints of the environment make a new language a better choice, a choice with fewer compromises.

So, yeah, Swift borrows liberally from “Rust, Haskell, Ruby, Python, C#, CLU, and far too many others to list.”  But it couldn't be any of those, for the most basic of reasons, that requirement of interoperability.

The Good, the Bad, and the Ugly

Are you ready for a subjective laundry list?  Let’s do it!

The Good

  • Interactive REPL
  • Semicolons?  We don't need no semicolons!
  • Unicode
  • Immutable and mutable bindings (let and var)
  • Type inferencing (can handle recursion in some cases that Scala can’t, it would seem)
  • String interpolation
  • Array and dictionary literals
  • Pattern-matching!
  • Closures and first-class functions
  • Tuples
  • Explicit override
  • Optional
  • Generics with type constraints
  • Protocols
  • Extensions
  • Arithmetic overflow detection
  • Mandatory named parameters (sometimes, you don’t want people to have the option of screwing up)
  • Trailing closures, I think. I’m not entirely certain whether this is the right approach; but it does seem useable.

The Bad

  • Immutable arrays aren’t.  This is documented behavior, but it may be changed.  (Update: It was changed.)
  • Documentation is vague on when/whether dictionary keys are immutable.
  • Optional is baked in as a special case.  Don’t expect to use the same syntax for anything you might conjure up yourself.
  • Mutable structs that are always passed by value.  How can this be a good idea?
  • It seems that genericity and protocols don’t play well together.  I am not sure how well I understand the issue, to be honest, since I have not actually written any Swift code.
  • The two-dot/three-dot half-open/closed range syntax is a horrible mistake. (Update: This has been changed, to use ..< and ..., which takes it out of Bad but maybe still leaves it in Ugly.
  • Mutable strings
  • Generic type constraints are limited, with …
  • No explicit covariance/contravariance for generic type parameters
  • Not really functional, although the author of that piece is not criticizing, just observing 

The Ugly

  • The test-optional-and-bind syntax (if let) is pretty ugly to my eye.  For that matter, so is the pattern matching bind (case let).  My brain does not want to see that let in the middle of a statement.
  • I just don’t think switch is the best name for the pattern matching statement.  Pattern matching is so different from what a C switch does.  (Perhaps a case of trying too hard to appear familiar.)
  • External names for parameters.  I don’t object strongly to the idea, but the syntax leaves something to be desired.
  • String mutability is determined by the kind of binding?  That is weird and confusing.
  • C-style for loops are probably unnecessary and may encourage bad style.
  • Labeled statements and directed break/continue.  Bleah.
  • var parameters.  Why bother?
  • inout parameters probably exist only for compatibility with existing code.  A necessary evil, I suppose.
  • The keyword in for separating closure header from body grates on me.
  • Using static/class modifiers instead of having a single keyword for both cases.

Will It Fly? (sorry)

Contributions on Apple’s developer forums from the Swift team make it clear that Swift is a work in progress.  The Swift creators are interested in feedback and they want to improve the language.

Swift could well achieve one of its open secret goals: to become a teaching language.  (The benefit for Apple is obvious.)  The playground feature is pretty nifty, and I can easily imagine wanting to use it if I were introducing some basic programming concepts.

Would Swift spread beyond the Cocoa domain if the implementation were opened up?  That’s hard to say.  As a language, it does not seem compelling compared to my current favorite, Scala.  The native code compilation has some appeal, but the JVM has a pretty good JIT these days.  The reference counting storage management could be a big deal in certain applications, but the developer overhead in manually managing cycles is hard to justify outside of narrow domains.

For its intended use, however, I would say Swift is a pretty good bet to be successful, and probably make a lot of Cocoa programmers more productive.

Wednesday, January 29, 2014

Scala: What’s not to like?

What if you like object-oriented programming and you like functional programming and you like having an interactive interface and you like the Java ecosystem and you like static typing (but you wish the compiler wouldn’t make you be explicit all the time about type declarations)?

Scala lives at the intersection of all those things and, after spending some time with it, I have to say it’s pretty good.

Maybe I’m just weird, but my initial reaction to Scala was that it felt enough like Python to make me happy.  I like Python, but I’ve never tried to use it for anything big—it doesn't feel like I would be comfortable trying to move around in a lot of Python code.  Scala mostly succeeds at feeling simple enough to start diving in scripting-style while having the muscle to build anything you might want a “systems language” for.

Things I Like

A type system that works

I still recall when I first learned that Java's type system made arrays covariant, so that a program could pass the static type checker, not do any explicit dynamic type casting, and still generate run-time type exceptions.  Yay (ironic).  It took years for Java to add generics (parameterized types), and by that time the compatibility concerns meant that it was too late to do things right.  So, Java has static types with generics, but they're not as flexible, powerful, or reliable as you might like.

The type inferencer is good enough that most declarations are for humans, not the compiler.  (There are exceptions, of course.  The most common is that the compiler needs an explicit return type for a recursive function.)

The truth is that the time is long past when I could read a paper about F-bounded polymorphism and imagine that I understood it.  But the Scala type system seems to work in the real world.  Yes, sometimes the error messages are not as helpful as I'd like.  But I'll take that in exchange for what I get in return.

Interactive read-eval-print

Is this really that important?  I think so.  All programming is exploratory to some degree, and all exploration is interactive.  Being able to type in a few lines and see if they produce the result you expect is invaluable.

Value classes

You want zero-overhead wrappers for primitives like integers and doubles?  You want to add behavior to integers by subclassing without any overhead?  You can do that with the combination of value classes and implicits.  This can be surprisingly handy.

Java interoperability

So, Java is not my favorite programming language, but it has an enormous ecosystem.  Scala can plug into that by virtue of its JVM implementation and Java interoperability.  

The interactivity comes into play here as well: Whenever you use someone else’s library, you have to able to do some exploration.  I’ve used both Rhino and Jython as ways to experiment with Java APIs, but they don't feel like an especially good fit, maybe because of the dynamic typing.  Scala works well for this.

Decent IDEs

Well, I've only used IntelliJ IDEA, but people seem to think Eclipse is also fine for Scala.  This isn't so much a good thing about the language as a necessity.

Call by name

This is something that really got done right.  I suppose it’s just syntactic sugar for automatically generating no-argument lambda expressions, but it adds the right kind of control structure extensibility without being, well, weird.

A better Java?

I've seen people say that Scala is to Java as C++ is to C.  Sometimes they mean that as a negative, and sometimes as a positive.  I agree that C++ adds a lot of complexity to C, but I also think it makes a more expressive language that, used with care, makes for efficient, maintainable code.  And that's pretty much how I view Scala.

Things I Don’t Like

There's more than one way to do it

This is, I guess, an unavoidable result of a multi-paradigm language.  Somehow it seems worse in Scala than it does in Python.  Part of that is the syntactic sugar aspect, where there really isn't more than one way to do it, but there's more than one way to express what you're doing.  But another part is that Scala really lives in two worlds: the Java world of mutable data structures and the functional world of immutable data structures and higher-order functions.  You can write code in either style and still be idiomatic, although people are likely to steer you more toward the functional style.

The existence of both traits and abstract classes is another side of this.  Even the language's creator has trouble explaining exactly when to use which.

Strings

The idea of a programming language that doesn’t actually have a native definition of strings is just bizarre (“Scala’s String class is usually derived from the standard String class of the underlying host system (and may be identified with it).”).  Scala strings are Java strings, and if some behavior of Java strings is just not quite Scala-like, that’s too bad.  Yuck.

AC adapter not included

Here's where I think the Python comparison makes Scala look bad.  The Python slogan “batteries included” refers to the standard library, which is full of useful stuff.  Scala native libraries are mostly collections and utilities.  Scala has direct access to all of the Java standard library, but there are no standard wrappers to make it easy to use those Java classes from Scala, and the Java standard libraries lack a lot of useful things that can be found in the Python library.

JVM crap

Thanks to the way Java handles generics, Scala has to live with the JVM's type erasure.  That's a nuisance, and it makes some code clumsier than it ought to be.

Scala has to live with Java's exception and class/interface models, too.  The former isn't hopeless, although interoperability means dealing with checked exceptions.  The class/interface model means that the mapping between Scala types and Java types can be surprising.

That Darn @!

There really are only two things that @ is used for (I think), annotations and pattern matching, but somehow the many different ways this can manifest just makes me worry every time I see an @.  Annotations on classes, methods and variables are simple enough, but annotations on types and expressions get a little confusing.  The annotation is not always where you might expect it to be, and it's easy to lose track of what's going on.  The cognitive load for distinguishing an annotation from a patten match type constraint and figuring out what exactly the annotation applies to is higher than I would like.

Things I’m Just Not Sure About

Implicit magic

Scala's mechanisms for implicit parameters can do amazing things.  Implicit parameters are the way that generic methods on collections can return new collections of the right type, for example.  And the “pimp my type” pattern (using implicits) makes it easy to add behavior to an existing implementation.

Implicits are pretty carefully controlled: the compiler will complain about ambiguity, so the risk of surprises is fairly low (but not zero).  But implicits are also a temptation to hide parameters that could be explicit—just to reduce the number of characters that have to be typed (silly) or read (maybe good, maybe bad).  My bias is with Python: “Explicit is better than implicit.”

Syntax follies

Scala will let you define your own special-character sequences as names.  This can and does lead to people doing things that, in the guise of “domain specific languages,” are really just silly.  But if I ever need to, I can define the UFO operator as <-^->.

I think the rules for infix syntax are the same for both special-character names and ordinary names (+ or plus).  But the rules for when parentheses are mandatory are not easily internalized, especially for parameters that are functions.  It is not always clear why sometimes parentheses are necessary, sometimes braces are sufficient, and (maybe?) sometimes you need both.

XML baked in

There seems to be consensus that this was probably not the best idea.

Churn

Scala is very much a living language, and the rate of change is kind of high.  I'm not sure whether I think that is bad or not.  It will certainly depend on your use case.

Random Stuff

The Scala meaning of yield is quite different from the Python meaning of yield, since Scala doesn’t have generators and Python has different for-comprehension syntax.  That threw me at first.

I guess functional programming gurus have no problem with it, but I always have to translate flatMap into the equivalent nested for comprehension, which makes code using flatMap a little harder to read.

Companion objects are pretty handy, but it’s easy to confuse the companion object’s apply method with a constructor.  (The classic example is the difference between Array(0) and new Array(0).)

Also, nothing actually guarantees that the unapply method used in pattern matching is consistent with the apply method (or case class constructor).  That’s an opportunity for confusion.

Summary (or “TL;DR” if you prefer)

Scala is actually pretty cool.  It has its warts, like any programming language, but there is a lot there that feels right.  It’s too bad it doesn’t have more traction.