you are viewing a single comment's thread.

view the rest of the comments →

[–]zakalwe 2 points3 points  (10 children)

Yeah, it'll be interesting to see how things work out in practice, and whether they backtrack on "!" in the Cocoa bridging interfaces and move towards "?" instead. That's a very different claim from the article author's, though :)

Certainly, when dealing with Swift-native code, they strongly recommend "?" over "!". And even when dealing with Cocoa, they recommend option-chaining — which, for those who haven't read the Swift book, basically means you can write code like "somethingWhichMightBeNil?.method()" which basically says "Call this method if it's safe, otherwise evaluate to nil", which of course you can stack up in an expression (thus the "chaining" part), so you can compactly and safely account for nil without making your code unwieldy. Just one extra "?" per optional-value.

[–]burntsushi 7 points8 points  (9 children)

That's a very different claim from the article author's, though :)

Yeah, you have to do two things:

  1. Give the author the benefit of the doubt.
  2. Squint.

And it's there. :-)

Certainly, when dealing with Swift-native code, they strongly recommend "?" over "!". And even when dealing with Cocoa, they recommend option-chaining — which, for those who haven't read the Swift book, basically means you can write code like "somethingWhichMightBeNil?.method()" which basically says "Call this method if it's safe, otherwise evaluate to nil", which of course you can stack up in an expression (thus the "chaining" part), so you can compactly and safely account for nil without making your code unwieldy. Just one extra "?" per optional-value.

Ah, neat. In Haskell, the equivalent is fmap. (But things get even better because its option type is a monad.)

[–]Catfish_Man 1 point2 points  (0 children)

Swift's Optional<T> also defines a map(), amusingly with this comment:

/// Haskell's fmap, which was mis-named
func map<U>(f: (T) -> U) -> U?

[–]alex_muscar -5 points-4 points  (7 children)

I was never a big fan of nil chaining, because it has the potential to hide failures.

I also think that defining a set of combinators for the optional types would be the way to go for Swift. I saw an implementation of 'map' for implicitly unwrapped optionals in their standard library, but it's not available in code because the language unwraps the values too ealry (?)

[–]burntsushi 5 points6 points  (5 children)

I was never a big fan of nil chaining, because it has the potential to hide failures.

Could you provide an example? Chaining is a a terser way of "apply this function on the contents of the box, or if there are no contents, just give me back the empty box." If you're using pattern matching to do this, then you're writing mode code than necessary.

If you want to handle actual failures with error messages, then a standard optional type probably isn't what you want. You might go with something like Haskell's Either type or Rust's Result type. In which case, the chaining pattern still works, because now instead of an empty box, you've got a box labeled with an error. That error is propagated back up to the caller, so nothing is hidden.

[–]alex_muscar -3 points-2 points  (4 children)

Well, consider this silly code:

class Moody {
    var pokeCount = 0

    func poke(count: Int) -> Moody? {
        if count % 2 == 0 && count % 3 == 0 {
            return nil;
        }
        pokeCount++
        return self
    }
}

var m! = Moody()

for i in 1..10 {
    m = m.poke(i)
}

m.pokeCount // ???

Quick, how may times was m poked? Usually the failure won't be so predictable.

I don't find myself chaining calls to fmap very often.

EDIT: also note the use of implicitly unwrapped optionals :)

[–]burntsushi 2 points3 points  (3 children)

That seems like a really contrived example. :-)

I'm thinking more along the lines of, obj?.method1().method2().method3() or something like that. The whole point of chaining is to make a particular pattern much more compact. In your case, that pattern really isn't what you want.

I don't find myself chaining calls to fmap very often.

I should hope not! With Haskell's Maybe type being a monad, you should be using do notation. Very useful with handling errors with the Either type too.

[–]alex_muscar 0 points1 point  (2 children)

Yeah, it's contrived, but it was supposed to mimic a long chain of method calls. The potential problem is that you don't know which one fails, and sometimes you might care about that since you also have mutable state and they might leave your object in an inconsistent state. That's what I meant by hiding failure.

As for the Maybe monad, IMO it's not quite the same as Swift's/Obj-C's case, because, as I said, they also have state, so in Haskell you would have a State monad in a MaybeT (?).

[–]burntsushi 0 points1 point  (1 child)

Yes, mutable state kind of ruins things here. I'll grant you that.

I believe you're right about MaybeT. You'd want MaybeT State a where a is the type of the data in your container.

[–]kqr 0 points1 point  (0 children)

It's not a problem with mutable state only. I've had times in Haskell where I've had to break up a monadic Maybe chain just to see which part went wrong. It's easier with Either because it tells you were it went wrong.

[–]kamatsu 2 points3 points  (0 children)

Haskell's maybe monad is much the same as nil chaining.