Swift: Things I really wanted that won’t make it
ericasadun.com
ericasadun.com
I decided to start using it this week and after learning the requisite umpteen fiddly syntax idiosyncracies was startled to discover how little Swift supports you where traditional platforms (Java, C#, Ruby, Python, Node, Go) you got for free: a dang HTTP server, for example, requires you to write several[0] thousand[1] lines[2] of[3] code[4].
For a batteries-included language, Swift has a long ways to go before thinking about "defeating Go on the web".
[0] https://github.com/PerfectlySoft/Perfect/blob/master/Sources... [1] https://github.com/PerfectlySoft/Perfect/blob/master/Sources... [2] https://github.com/PerfectlySoft/Perfect/blob/master/Sources... [3] https://github.com/PerfectlySoft/Perfect/blob/master/Sources... [4] https://github.com/PerfectlySoft/Perfect/blob/master/Sources...
No, it means that it's a functional language. Please consult google if you are not sure what that means.
> "pretty good type system" (which means it has generics?)
No, it means that it has a more advanced type system than any of the languages you mentioned. It's most likely the most popular language with a type system that can be considered somewhat advanced.
There might be idiosyncrasies but like compared with say JS or whatever it's still miles ahead.
Yeah, the standard library is somewhat lacking but like that will be fixed. The language is very solid though.
Also don't judge a language by lack of an HTTP server in the standard library.
Please inform me what Swift's type system has over, say, Java as I don't know of anything and neither does a cursory Google.
The lack of an HTTP server is only an example. Let's see you parse JSON, or stream a Unix socket, or any of countless basic things that even Node includes in its standard lib. Swift doesn't really have much of anything except some types and traits: https://developer.apple.com/library/ios/documentation/Genera...
One way that Swift's type system is more advanced than Java is the ability to define extensions to protocols (interfaces) constrained to specific types, i.e. you can add "average" to "SequenceType where Generator.Element == Double". You can also use this to provide default implementations for protocols, but only in the case of specific associated types.
Perl 6 did some work on this. I'm not sure if it's fully supported yet, but the design docs[1] go into detail on how it's supposed to work, so it may be worth looking at.
> Method Cascades I suspect you might find disambiguating your method/attribute access easier in your "with" syntax if you make it match what swift appears to do for anonymous variables, use $0. (pardon my unfamiliarity with Swift, I may be mistaken on some obvious things).
E.g. Instead of
with let task = NSTask() {
launchPath = "/usr/bin/mdfind"
arguments = ["kMDItemDisplayName == *.playground"]
standardOutput = pipe
launch()
waitUntilExit()
}
do with let task = NSTask() {
$0.launchPath = "/usr/bin/mdfind"
$0.arguments = ["kMDItemDisplayName == *.playground"]
$0.standardOutput = pipe
$0.launch()
$0.waitUntilExit()
}
That way if you want to use some non-related variabled within the block, or the output of an attribute or method as the input of some other attribute or method, it's not ambiguous. func with<T>(value: T, f: T -> Void) -> T {
f(value)
return value
}
Which you could then use like: let task = with(NSTask()) {
$0.launchPath = "/usr/bin/mdfind"
$0.arguments = ["kMDItemDisplayName == *.playground"]
$0.standardOutput = pipe
$0.launch()
$0.waitUntilExit()
}
The `let task =` part would be optional, and you could leave it off if you don't need to use the variable later.Your solution is actually simple enough that I'm not sure there needs to be a change to the language, unless there's some special behavior they can and should impart that we aren't thinking if.
That said, I don't write swift, so feel free to take my opinion for whatever you think it's worth. :)
task ← NSTask new.
task setLaunchPath:'/usr/bin/mdfind';
setArguments:#('kMDItemDisplayName == *.playground');
launch;
waitUntilExit.
Aside from the ';', no special syntax needed. And unlike other Smalltalks, there is a parallel with '|' for composition instead of cascade (so send the next message to the result instead of the receiver of the first expression), which helps disambiguating keyword syntax without requiring parentheses: 'a' stringByAppendingString:'b' | stringByAppendingString:'c'.
This is especially useful when building expression incrementally, as it doesn't require going back to the start of the expression. Of course, stringByAppendingString: isn't such a good example because the ',' message makes this quite a bit more compact: 'a','b','c'Example:
with obj {
.field = abc;
xyz = .foo(12);
.method();
}
This would have several advantages:* easy for syntax completion
* can access names that are not in the innermost with-scope
* can access names at outer with-scopes using .. and ... etc
There has been discussion about statically typed errors on the evolution mailing list — some core team members said they may look into in the future and others seeming more doubtful of its usefulness.
Why do you think a typed error handling model is better than introspecting the error at the catch site, I’m not sure I buy it?
You can use Any for everything, and it can even be totally safe if you use as? correctly, but it's obvious why no one does that. It's not obvious to me why people are fine with that situation for errors - again, the catch-all handler issue mentioned above, and the bizarre special-casing of NSError.
func throwing() throws FooError -> Int
or even: func throwing() throws (FooError, BarError) -> Int
to create an implicit sum type, I'd find it acceptable, but not being able to tell what types of errors an API will throw is far from ergonomic - you'll need a catch-all block if you use anything but NSError[1] or ErrorType, even if you know the function only throws FooErrors.[1] this is especially weird because NSError is just some random Foundation class, it's not anything inherent to the Swift language.
[1] Or to put it in more practical terms: Imagine two classes/interfaces A and B where B subclasses A. Any method on B that overrides a method on A is free to accept a "more restricted" parameter than the method on A, but since it (presumably) does something more specialized it must also be able to throw more exceptions that A's method was. (Maybe it's accessing files and needs to be able to throw FileIOError, or whatever.)
I agree with your assertion that this is part of what makes explicit throws a pain, though. I hadn't thought about it that way, thanks for the insight.
Functions would be free to just "throws ErrorType" (probably the default for blank "throws", to avoid breaking backwards compatibility) if they really wanted to though, just like they can accept and return "Any", and the behavior would be identical to today.
I prefer Result though, which just solves the problem of disjoint error types by using "mapError" and a sum type.
let task = NSTask()
..launchPath = "/usr/bin/mdfind"
..standardOutput = pipe
..launch()
..waitUntilExit() let task =
if you don't want to use that object any further.All my work is client work. There have been a bunch of occasions where I've had to work around some weirdness of an Apple SDK by creating my own subclass. I could see Apple releasing an SDK and many of the classes being @final. Then I'm stuck in a situation where I can't provide a deliverable to the specifications a client wants due to a limitation in an SDK.
That's my only fear though really. Rarely do I personally do a lot of subclassing unless I absolutely have to or it really does make sense to.
Apple could (and should, I think) release future SDKs that use all-final classes and require composition instead of the incredibly fragile base classes that currently make up UIKit and Foundation - they'd just add the "final" keyword.
Since UIKit is inheritance-based currently, in a final-by-default Swift, it would be imported as "subclassable", "nonfinal", or whatever. Nothing would change other than the default for newly-written Swift code.
final by default doesn't prevent someone from manually adding final to their classes.
But the main problem with subclassing is: https://en.wikipedia.org/wiki/Fragile_base_class
This is how it should work in many production environments: Are you 100% sure about that? No? Don't do that! Then start asking why you can't be sure, then fix that. Rinse, repeat.
By using the tools that Swift provides - preferring value types, and falling back on final classes, I can much more easily deduce what my changes will do.
Non-final classes create an additional public API that framework authors need to support - the ability to change any behavior. Reducing the surface for potential errors makes frameworks and their clients more robust.
No disagreement here.
Non-final classes create an additional public API that framework authors need to support - the ability to change any behavior. Reducing the surface for potential errors makes frameworks and their clients more robust.
Since Smalltalkers knew all their code was "surface," there was motivation to keep things very encapsulated. (Perhaps this is part of why the Law of Demeter was so big in that programming culture.) Synergistic with this, was the heavy use of the very powerful debugger. If your codebase was mostly relatively stateless or very well encapsulated, you could time-travel with ease in the debugger by unwinding the stack, recompile your method in place, and continue on. Conversely, if you wrote code that didn't have those qualities, your fellow programmers would get annoyed at you for making their lives harder and their tools much harder to use.
Increasing the surface makes frameworks more flexible and necessitates good design throughout. Is there a trade-off? Sure. The really good Smalltalkers spent lots of time reading code and exploring stuff in the debugger/browsers. And sometimes, you could be stymied because you couldn't rule out stuff and risk a blow-up in production. And to be fair, in my estimation, Smalltalk projects were less robust -- but got fixed really quickly.
Nowadays, I think the sweet spot would be in a simple language with super fast compile/edit/test cycles, with equally powerful debugging, and with type annotations.
Smalltalk-like. Go back one more step and the influence almost certainly comes from there. (Drink!)
Everything should return a value, there shouldn't be any statements.
Bring back ++ and -- and currying, even if just as syntactic sugar that can be converted into the regular syntax by a tool, for those choose to write in it.
I appreciate Swift's philosophy and agree with their decisions so far, but there's a certain beauty to concise code (when it feels like writing maths) and it'll certainly make prototyping in Playgrounds more fun and, shall we say, swift.