Thanks for all your replies.
You're right that it takes time to get used to new ways of doing things when you have done them a certain way for a long time. The comparison with Java and C code formatting is a good example and Apple did something similar with Objective-C switching the square brackets. This familiarity is important to productivity, code maintenance and portability and likely contributed a lot to Java's popularity. I'd like to see Swift become as popular a language as Java.
Having these safety features are useful when they are needed but hinder productivity when they are always required. Code linters are the same. They are nice to maintain neat code but when you need to meet a deadline, it's quicker to turn them off, get the job finished and do the cleanup after.
I agree it can be helpful on occasions where there's a clear problem and a clear fix, usually on single variables. The main problem I've seen is when dealing with 3rd party code and trying to access nested properties where the compiler will say that I can't access one property as it's not unwrapped and recommend a '?' and then I think hey that's nice but when I add that, it then goes 'ah but now that's an optional, you also have to do this other one' and then I fix it and then it does the same thing somewhere else and it just cascades through the codebase and it does this constantly, interrupting my workflow as I work on a project.
When you are in brainstorming mode and trying to get a project off the ground and get familiar with the language, it creates a roadblock to learning the language and productivity. You end up thinking about language design instead of the project. That's why languages like Typescript, Python and Javascript are so popular because they get out of the way and just let you work.
Thanks for the detailed reply. That code used to compile in Swift, I think it maybe doesn't now due to removal of implicit unwrapping in some places?
The main issue I have with optionals is not that it requires handling nil types, it's that it requires me to keep track of when something is optional and when it's not, that takes a lot of work on a big codebase because I have to trace back through the path that led to that call. There is an example on this page:
https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html
if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
print("John's building identifier does not begin with \"The\".")
}}
When I work with things like JSON objects (or databases as @Hacksaw mentioned), the entire data object is untyped and has to be mapped onto a type-checked structure at runtime.
Wouldn't it be easier in code to just assume that everything can be nil? Why write the following:
if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
instead of:
let beginsWithThe = john.residence.address.buildingIdentifier().hasPrefix("The")?
and have the compiler warn that this is potentially a nil value and that it should be checked with a conditional or even require a default assignment to a non-nil type?
This would be similar to try/catch error handling. When I develop projects, I normally either assume that everything can be nil at some point or have a confidence that something will always be non-nil and if not, something went wrong.
Swift's strictness takes away my control over that and while it might make the code safer, it slows me down and it encourages me to force unwrap things just to get the project to build.
That site links to a StackOverflow search for Swift crashes that has 60 pages of results.
I guess you could say that's evidence for why it's important to write the syntax correctly to avoid those crashes but the reason people do that is because it's not intuitive nor easy to maintain.
Examples of non-intuitive things are optional optionals:
"because the user is optional it uses optional chaining, and because getMessages() can throw, it uses try? to convert the throwing method into an optional, so we end up with a nested optional. In Swift 4.2 and earlier this would make messages a String?? – an optional optional string – but in Swift 5.0 and later try? won’t wrap values in an optional if they are already optional, so messages will just be a String?."
It's good that the language is evolving to simplify these things but why did it take 5 revisions to do that? The fact that someone ever thought that nested optionals was a good idea shows that nil-checking is being overthought. The language created this problem in the first place by having such strict adherence to nil-checking and is now trying to fix it in retrospect and breaking code.
It would have been better to start with no strict adherence to nil-checking and add the strictness where it was needed. Given that most languages don't offer this and most developers don't use it suggests it's not that big of a deal.
That's why I think having a strict/non-strict mode (like in Javascript) would make things a lot easier, especially in mixed language environments. Enterprise developers who absolutely need the strictness can require it in their code. Indie developers or multi-platform developers who need to keep code synced or kids just learning to code in school can turn it off. All this would do is treat everything as implicitly unwrapped and it can warn of mistakes instead of fail to build and run and can be done on individual classes, lines or files.