Here's an example about Haskell. Go to https://tryhaskell.org and enter these:
3
div 6 2
3 + 1.0
(div 6 2) + 1.0
The first line returns 3, and so does the second line. The third line returns 4.0. The fourth line fails with this error: No instance for (Show a0)
arising from a use of ‘show_M552168474310802709512215’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Show a => Show (Const a b)
-- Defined in ‘Control.Applicative’
instance Show a => Show (ZipList a)
-- Defined in ‘Control.Applicative’
instance Show GeneralCategory -- Defined in ‘Data.Char’
...plus 106 others
In the expression:
show_M552168474310802709512215
(let e_16210 = (div 6 2) + 1.0 in e_16210)
In an equation for ‘e_155216847431080270951221555216847431080270951221516210621016210’:
e_155216847431080270951221555216847431080270951221516210621016210
= show_M552168474310802709512215
(let e_16210 = (div 6 2) + 1.0 in e_16210)
In the expression:
(let
e_155216847431080270951221555216847431080270951221516210621016210
= show_M552168474310802709512215 (let ... in e_16210)
in
e_155The type of `3` gets inferred as `Num a => a` because there's nothing to constrain it further.
The type of `div 6 2` gets inferred as `Integral a => a` because the type of `div` is `Integral a => a -> a -> a`.
The type of `3 + 1.0` gets inferred as `Fractional a => a` because of the decimal point.
Then the same thing happens in the last statement so the type of the subexpression `(div 6 2)` (still) gets inferred to `Integral a => a` and the type of `1.0` (still) gets inferred to `Fractional a => a` because of the decimal point.
So at that point you have an expression that's trying to add something constrained to being a whole number to something constrained to being a fractional number and that is ill typed because there is no type you could choose for the overall expression that would meet both sets of constraints.
Edit: When I first replied there was text at the bottom of the message saying that no one could explain why this happens. My comment was addressing that portion of the original message. The behavior itself is not surprising for the same reasons.
Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 18 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
In a stmt of an interactive GHCi command: print itGHC is a little better.
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> (div 6 2) + 1.0
<interactive>:1:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 18 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it fn main() {
println!("{}", (6 / 2) + 1.0);
}
fails to compile with the following error message: error[E0277]: cannot add a float to an integer
--> src/main.rs:3:28
|
3 | println!("{}", (6 / 2) + 1.0);
| ^ no implementation for `{integer} + {float}`
|
= help: the trait `std::ops::Add<{float}>` is not implemented for `{integer}` fn main() {
println!("{}", 3 + 1.0);
}
Also fails with substantially the same error message. Rust is consistent that you can't add integers to floats. Haskell's looseness around numeric literals means that it isn't.Mind you, Rust does have that kind of looseness within the integers, so this:
fn three() -> u8 { 3 }
fn one() -> u16 { 1 }
fn main() {
println!("{}", 3 + 1);
println!("{}", 3 + one());
println!("{}", three() + 1);
println!("{}", three() + one());
}
Fails to compile only on the last println.For teaching beginners, I very much question the amount of polymorphism in Haskell, and one floated idea has been to use a custom Prelude with much less of it for teaching purposes. Alternatively, I would probably just tell students to read an error like that as "you did something wrong." I would say the same for any student faced with a Java stack-trace showing how their null value threw an exception deep inside library code.
If you're defining instances, you're already an "expert", so why are you defining instances that are "hostile" to your beginner students?
If people in the ecosystem are defining hostile instances, you should probably just ask them to stop it...?
EDIT: Also, isn't failing to compile the almost-optimal case? It certainly doesn't fail at runtime.
There's a similar situation with the now classic:
Prelude> length ['a','b']
2
Prelude> length ('a','b')
1
Here, beginners are potentially being burned by the fact that "length" is polymorphic with the type: length :: Foldable f => f a -> Int
which is not a type I want to explain to beginners. At the same time, as an expert, I want length to be polymorphic and don't want to sacrifice its polymorphism for the sake of beginners.I think the problem there is really calling it 'length'.
Prelude> :t (div 6 2)
(div 6 2) :: Integral a => a
Prelude> :t 1.0
1.0 :: Fractional t => t
Bonus: there is a language extension which does you do this: https://www.schoolofhaskell.com/school/to-infinity-and-beyon...
If your point was that the error messages could be nicer, I agree.
Solution:
(fromInteger (div 6 2)) + 1.0
I would argue that programming languages are subjective. There are many different equally viable ways to model the world and programmers should pick the one that aligns with how their brain works.
I'm not convinced it's possible to objectively rank programming languages from best to worse. They are not fundamental properties of the universe simply mental models devised by humans for humans.
I saw something really cool on the weekend. PHP on the client side as a compiled wasm. This allows someone to go severless and run a cms in the client.
That's a new use for an existing language.. is it suitable for everything? Not until it is.
Better for what, for whom? Every programming problem is basically a unique problem of its own -- else the solution would already exist as source-code. There's no need to re-program the wheel.
Therefore the generic question of "what programming language is objectively better" does not make much sense, unless we qualify the question with: better for what, better for whom.