You lose this ability if you start letting the compiler decide that `Int` is as good as `Maybe Int`. Or if an `Async (Async String)` may as well be an `Async String`.
That's not to say it's not easy to transform (just a few keystrokes), but explicit beats implicit when it comes to casting (in any language).
[1] Does this work?
var x = 1 == 2 ? Optional.of(2L) : Optional.empty().map(y -> y + y);
// Operator '+' cannot be applied to 'java. lang. Object', 'java. lang. Object'
How about Optional<Long> x = 1 == 2 ? Optional.of(2L) : Optional.empty().map(y -> y + y);
// Operator '+' cannot be applied to 'java. lang. Object', 'java. lang. Object'
or even Optional<Long> x = 1 == 2 ? Optional.of(2L) : Optional.empty().map((Long y) -> y + y);
// Cannot infer functional interface type
No, we needed Optional.<Long>empty() instead of Optional.empty() to make it workI was thinking more like explicitly telling the compiler that an implicit cast is OK, in other languages done by implementing the implicit cast operator for example.
edit: but if I understood you correctly, Haskell just doesn't support any implicit casting?
If no other info is given, it will treat `3 + 3` as Integer + Integer (and emit a compiler warning because it guessed the type).
With `(3 :: Int64) + 3`, the right 3 will resolve to Int64. Same if you swap their positions.
`(3 :: Int64) + (3 :: Int32)` is a compile error.
"Text literals" can become a String, a Text, or a ByteString if you're not explicit about it.
> implicit cast operator
Wouldn't that make it explicit?
No, the casting is still done implicitly. That is I can make the following compile fine in Delphi if I add an implicit cast operator to either Foo or Bar:
Foo x := Foo.Create();
Bar y := x;
If neither of them have a suitable implicit cast operator defined, it will of course fail to compile.Just an example, nothing unique about Delphi. You can see an example of the operator definition here[1].
[1]: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Oper...
Not without explicitly enabling `OverloadedStrings`!
https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/over...
data Maybe a = Just a | Nothing
So to make an X a Maybe X, you'd put a Just before a value of type X.
For example:
one :: Int
one = 1
mOne :: Maybe Int
mOne = Just one -- alternatively, pure one since our type signature tells us what pure should resolve to.
Reason we can do this is because Maybe is also an Applicative and a Monad and so implements pure and return which takes an ordinary value and wraps it up into an instance of what we want.
Swift interestingly doesn’t require this, but only because Optionals are granted lots of extra syntax sugar in the language. It’s really wrapping it in .some(x) for you behind the scenes, but the compiler can figure this out on its own.
This means that in swift, changing a function from f(T) to f(T?) (ie. f(Optional<T>)) is a source-compatible change, albeit not an ABI-compatible one.
I'd call that explicit casting. Implicit casting would be
mOne = one
Compiler already knows what "one" is, it could insert the "Just" itself, no? Possibly due to an operator defined on Maybe that does this transformation?That is, are there some technical reasons it doesn't?
Or is it just (no pun inteded) a language choice?
It seems you're asking for code to be both null safe and not null safe simultaneously.
Having a language just decide that it would like to change the types of the values flowing through a system is wild. It's one of the reasons that JavaScript is a trash fire.
Of course with great power comes great responsibility, so it's a tool that should be used sparingly and deliberately.
Now as mentioned I don't use Haskell, but that's why I like it in other languages.
I asked as I was curious if there was something that prevented this in Haskell, beyond a design choice.
If you make a breaking change to your API, then you should want your tools to tell you loud and clear that it’s a breaking change.
I also don’t agree that keeping the structure around values internally logically consistent “doesn’t really contribute to anything”. On the contrary, I think this idea is hugely important. How would your idea generalise? The compiler should just know that my `Int` should be a `Maybe Int` and cast it for me. Should the compiler also know that my `[a]` should be cast to a `(a, b)` because incidentally we’re fairly confident that list should always have two elements in it?
I think if this way of thinking is unfamiliar, then it’s a good reason to learn Haskell (or Elm, which is at least as good, or maybe better, for driving this point home).
The compiler should not "just" know it. It would know it because we told it how.
Consider a function that takes a float and returns a complex number. I then change the function to take a complex number ("Complex Float" in Haskell if I read the docs right), and returns a float.
I could then tell the compiler, by implementing an implicit cast operator, how to cast float to complex. The implicit part is then that the compiler tries it without me telling it to use the cast explicitly.
Then any code that worked with the old function should work perfectly fine using the modified function without modifications, since per definition the reals are contained in the complex numbers.
This is how I do it in several languages I've used.
Again, I don't know Haskell, so from the outside it looks like much the same as the float -> complex conversion.