For example, in Haskell, by default you can't compare an Optional with the value it wraps: `5 == Just 5` fails. But in Swift this works like you would want.
All that is to say that Options in Swift are a bit nicer than what you could get with pure ADTs. It's a similar story for Swift's syntax-level error handling vs type-level monadic error handling. (The downside of course is that the compiler has to be specially taught about these - but maybe you want that anyways, e.g. for Rust's null-pointer optimization.)
Haskell has bind (>>=) and do-syntax, rust has `and_then`. This is pretty standard with any ADT-supporting language.
> if-let
Most languages with ADT support have good pattern matching that subsumes if-let syntax and allows you to do other things as well. Swift's pattern matching, such as it is, is a bit of a mess.
> guard-let unwrapping
Haskell has `guard` and `MonadFail`, which address the use cases for this in a more principled way. `guard` for boolean matching (like equality) and `MonadFail` for structural matching (like pattern matching on a constructor).
Rust has (?) and `try`, which are syntactic sugar around a simple match/return statement that addresses most use cases of guard-let.
Obviously there are going to be small differences between this implementations, but Swift doesn't really excel here.
> `5 == Just 5` fails.
As it probably should. Supporting such implicit casting could lead to some obvious confusion and buggy behavior. Ideally, the fact that "a == b" typechecks should indicate that "a" and "b" are of the same type.
In Haskell, you would just do `Just 5 == x` if you want to check that something is, in fact, `Just 5`. If that really wasted a lot of space somehow, you can define something like `x ==: y = Just x == y`.
edit: FWIW, there's some overlap between '?' and 'guard let', but not that much. Using hypothetical Rust syntax, they'd overlap in this case:
guard let Some(x) = foo else {
return Err(GettingFoo);
}
better expressed as let x = foo.ok_or(GettingFoo)?;
…but if you want to return something other than a Result, or the ADT being matched on is something custom rather than Option/Result, there's no good way to make '?' do the job.Rust copied 'if let' and made it far nicer and more capable by allowing arbitrary fallible pattern matches to be the condition. Then Swift 2 copied that improvement back with 'if case'.
`context(e?)` becomes `e >>= \x -> context(x)`
It's totally fair to point out that Haskell is more principled - users can build Haskell's Maybe, but not Swift's Optional.
> Rust has (?) and `try`, which are syntactic sugar around a simple match/return statement that addresses most use cases of guard-let
They aren't really comparable. Rust's `try!` addresses one case: you have a Result and want to propagate any error to the caller. This is closest to Swift's `try`. But Swift's `guard` is much more flexible: it allows destructuring, boolean tests, multiple assignment, etc., and it also allows arbitrary actions on failure: return, exit(), throw, etc., with the compiler checking that all paths result in some sort of exit.
In practice this is used for all sorts of early-outs, not just error handling. It neatly avoids `if` towers-of-indenting-doom. I think the best analog is Haskell's do-notation.
There's the same tradeoff here. Rust's try! is a macro that anyone can build, while Swift's `guard` is necessarily built into the language. But any Swift programmer will tell you how much they appreciate this feature, unprincipled though it may be. Use it for a while and you become a believer!
> As it probably should. Supporting such implicit casting could lead to some obvious confusion and buggy behavior. Ideally, the fact that "a == b" typechecks should indicate that "a" and "b" are of the same type.
The principled stand! Both languages make the right decision for their use case. Swift's Optionals are much more commonly encountered than Haskell's Maybe (because of Cocoa), and so the language's syntax design optimizes for dealing with Optional. They're more central to Swift than its ADTs.
-- Probably nothing like in Swift
instance (Num a , Integral a) => Num (Maybe a) where
fromInteger x = Just (fromIntegral x)
main = print $ 5 == (Just 5) check :: Int -> Bool
check x = x == (Just x)
main = print (check 5)
I get a compile error: Main.hs:7:17: error:
• Couldn't match expected type ‘Int’ with actual type ‘Maybe Int’
• In the second argument of ‘(==)’, namely ‘(Just x)’
In the expression: x == (Just x)
In an equation for ‘check’: check x = x == (Just x)Why in the world would you want those two to be equal when they obviously don't represent the same thing?
That doesn't make sense, not even if they have the exact same memory representation, in which case I'm pretty sure it has been a compromise, which would mean you're still dealing with `null` with some lipstick on it, making that type behave inconsistently with other types in the language.
This kind of misguided convenience is exactly why equality tests in most languages are in general a clusterfuck.
This is the difference between normal people and theoretical computer scientists, summarized in one sentence.