Haskell error messages: come on
thecodedmessage.com
thecodedmessage.com
• No instance for (Num [Integer]) arising from a use of ‘it’
This one can be more straightforwardly understood to mean "you can't use a list of numbers as if it were a number". Also, starting in GHC 9.2, FlexibleContexts is enabled by default, so you'll get that better error the first time.What are extensions?
How do I find out about them?
Which should be enabled?
Is my code broken because of me?
Or because I should enable an extension?
Anyway. It’s all not caring about beginners the whole way down when it comes to Haskell and it’s community
Very rarely do I remember being misled with valid code just missing extensions.
error[E0277]: cannot add `[{integer}; 2]` to `{integer}`
--> src/main.rs:2:7
|
2 | 1 + [2, 3]
| ^ no implementation for `{integer} + [{integer}; 2]`
|
= help: the trait `Add<[{integer}; 2]>` is not implemented for `{integer}`
For more information about this error, try `rustc --explain E0277`.
Good error messages are important for adoption of languages with strong type systems. The complexity is already foreign enough for people who aren't familiar with type theory.These can all be fantastic things to have, but they involve tradeoffs. Those tradeoffs mostly make sense in the context of Haskell's original research focus. "Adoption" of the kind you're presumably referring to was an explicit non-goal for a long time, and still is among some users.
Edit: sometime else pointed out that more recent Haskell compilers give the error "No instance for (Num [Integer]) arising from a use of `it'" for this example, so this whole post may be kinda BS.
$ ocaml
OCaml version 4.13.1
# 1 + [2; 3];;
Error: This expression has type 'a list
but an expression was expected of type int
Though it is also more constrained than Haskell here Prelude> :{
Prelude| result :: Int
Prelude| result = 1 + [1,2]
Prelude| :}
<interactive>:3:10: error:
• Couldn't match expected type ‘Int’ with actual type ‘[a0]’
• In the expression: 1 + [1, 2]
In an equation for ‘result’: result = 1 + [1, 2]
Now I agree that this might not be an ideal thing to do when showing someone Haskell on a REPL, but I will argue in favor of annotating types at least for top-level declarations in a file. Especially for beginners, as learning to follow the types I feel is helpful to understanding the language and, in turn, its error messages.I am told that well before I learned Haskell, type inference was the Cool Thing. However, I am of the opinion that a strong type system like Haskell's is best used with guidance (annotations) from the programmer. The more powerful your types, the better a specification you can write. And with a better specification, the type checker can give you better insight into what's wrong when it gets stuck.
The key part is more expressive power makes good error messages harder.
Maybe Rust is right to make + overloadable, but not literals, which is how it makes the better error (as described in the piece), but I am not sure. Perhaps someone uses Complex number or Fixed Point literals a lot can chime in.
Racket's teaching languages are the wisest of all. Beginners and experts want different things, and there is no good reason we have our cake and eat it too, catering to their needs separately while allowing full code interop.
They benefit everyone.
There are only so many things that can fit into a human's head, and the skill required to decipher indecipherable error messages end up replacing some other skill.
And don't forget, these error messages will end up in the logs where you'll have to spend time figuring out what the hell happened.
Edit: typos from iPhone's increasingly unusable keyboard
I'd say I've run into performance issues professionally 5-10 times. None took more than a day to resolve.
Other than that, wolf-fencing it down is a good way to go usually.
A monad is just a monoid in the category of endofunctors, what's the problem?1) straight-up language tooling (profiling that breaks down heap usage in various ways)
2) wolf-fencing, which has nothing to do with haskell
In any event wolf-fencing very much appears to be Haskell specific based on some web searching. Or perhaps it’s a home improvement product? Several search results appear to indicate that[1].
http://coreygoldberg.blogspot.com/2008/12/wolf-fence-debuggi...
It's universally applicable. I've done it in C, Scala, Go, Java, Haskell..everything.
Perf-debugging wise - you basically remove parts of your computation and perform binary search to hone in on the parts that are on the critical path.
I agree that a lot of Haskellers come off as "what's the problem?" (including myself). But Haskell is a small, niche, weird language. You kinda need that attitude to dive deep into it.
Essentially what people intuitively do.
https://dixonary.co.uk/blog/haskell/small was most helpful
Examples? Sometimes there is a need for precising definitions.
and new issue in the Haskell Errors repo: https://github.com/haskell/error-messages/issues/42
$ ghci GHCi, version 9.2.1: https://www.haskell.org/ghc/ :? for help Main λ> 1+[2,3] <interactive>:1:1: error: • No instance for (Num [Integer]) arising from a use of ‘it’ • In the first argument of ‘ghciInteractivePrint’, namely ‘it’ In a stmt of an interactive GHCi command: ghciInteractivePrint it
In other words, this has already been fixed in GHC 9.2. More detail on the Reddit discussion of this post: https://www.reddit.com/r/haskell/comments/suinws/haskell_err...
What is `it`? Perhaps it is the argument for ghciInteractivePrint? That is what the error says. But what is ghciInteractivePrint that wasn't in the original code? I'm guessing an alias for print.
The error seems to be
No instance for (Num [Integer])
Which doesn't say anything about +So I'm still in the dark about this error message!
Ideally though it would be:
(+) has no instance for (Num [Integer])
The the improvement the GHC team made is already huge for those that have at least heard of Num, which I'd guess is anyone who's went through a Haskell tutorial.I don't think it's fair to draw the conclusion that the Haskell community don't care sufficiently about beginners from this, since only selected few of the community has the ability (and time) to work on GHC and improve the state of error messages.
This is false. Rust allows 1 to be a u8, an i16, etc. It does distinguish between integral and floating numeric literals, but integer literals are still polymorphic. The difference is just that in Rust, numeric literals are builtin rather than extensible.
Rust integers have a type (width and signedness) and you can't combine them without explicitly handling for overflow, etc. Any polymorphism through traits (eg. std::ops::Add) typically only operates over the same type as the source integer.
If types are left unspecified, it's only because type inference knows what type you mean. Since integers are frequently used in structs and function params (places where Rust requires typing), Rust knows the type when you perform operations on these variables. Outside of these cases, Rust will generally require you to disambiguate.
Rust is very deliberate about numerics.
Oof
Haskell doesn't overload its operators either (in the sense of there being multiple definitions with different type signatures); instead, it has a working type hierarchy that allows the same arithmetic operators to be used for all numeric types (through Num->Integral, Num->Real, Num->Rational and even Num->RealFloat->Complex). (1::Int) + 1.0 isn't well-typed in Haskell either, but at least (a + b) is a valid operation for all numeric data types. And, most importantly, as a developer you can define your own types that also implement these same operators.
OCaml does have polymorphic literals too: "%s" can be both type string and type formatter. It's just very limited and requires built-in compiler support, so it's not something you as developer can take advantage of in your own code. Although in practice I use OCaml more than Haskell, there's many times in OCaml where I wish it had more of Haskell's expressive type system.
Not great, but I think there are definitely no easy fixes here.
GHCi, version 9.2.1: https://www.haskell.org/ghc/ :? for help
ghci> 1 + [2, 3]
<interactive>:1:1: error:
* No instance for (Num [Integer]) arising from a use of `it'
* In the first argument of `print', namely `it'
In a stmt of an interactive GHCi command: print itThe errors have improved over time in my opinion, so your comment feels like confirmation bias.
> I am a big fan of strongly typed languages
> Now, in some “weakly typed” languages
What does "weakly" and "strongly" typed mean here? Did the author mean dynamically and statically? Or something else?
For example, in Javascript, you can add anything to anything, with rules that define which automatic conversions will be performed. Sometimes, hilarity ensues. In contrast, a strong language would require explicit conversions and throw an error otherwise.
In languages with ad-hoc polymorphism and operator overloading, you can emulate the Javascript experience by manually adding all the different cases yourself, in some sense 'weakening' the type system.
That's not the definition the author is going with considering that they're saying that Python (which requires explicit type conversions in most cases) is weakly typed.
From the replies here I'm seeing that there's no clear definition of "weakly" and "strongly" typed languages. I don't think it makes a lot of sense to use these terms.
I think it is, given that he links to https://xkcd.com/1537/ as stereotypical 'weak' behaviour.
Python sits somewhere inbetween Javascript and Haskell: For example, Haskell does not allow adding floats and integers, which Python will happily do despite a potential loss of precision.
Meanwhile Python, JS and many other weakly typed languages will happily add an integer and a float, maybe even a string and a number without complaints.
The advantage of the former are more rigorous compile-time checks, which might catch bugs where you added/compared the wrong values. But of course it's a bit more cumbersome especially if you're not used to it. I personally prefer it, I've had numerous cases where my Python script crashed 10 minutes into some data processing because it realised I tried to add two incompatible values.
Actually, that's a type error in Python, hence my question.
Wouldn't it be much easier to understand what is causing the error if I simply saw a short section of the source-code where I could FIX the error, by modifying something in the shown section?
What is the line-number where the error occurs? Show me the code, and where it is in relation to rest of the code.
I wonder if a "real REPL" would be useful for JavaScript. Or is it something that especially helps when working with languages like Haskell?
Prelude> 1+[2,3]
<interactive>:1:1: error:
• Non type-variable argument in the constraint: Num [a]
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a. (Num a, Num [a]) => [a]
The "line-number" is found on this line <interactive>:1:1: error:
Since it's in a REPL, it is referring to the first line you entered.The more confusing part is that the important error is found here
• When checking the inferred type
it :: forall a. (Num a, Num [a]) => [a]
where it is assumed that you know all REPL statements that don't bind to a variable are bound to a variable named "it". So the error is really saying that the whole expression 1+[2,3] has that inferred type, and that type is bad.If you load your example into a file and run GHC on it, it will produce a still confusing error message, but it will at least show you where the problems are
error-message.hs:3:11: error:
• No instance for (Num [a0]) arising from a use of ‘+’
• In the expression: 1 + [1, 2]
In an equation for ‘badType’: badType = 1 + [1, 2]
|
3 | badType = 1 + [1,2]
| ^^^^^^^^^
(I've omitted another, much longer error message it spewed out which is similarly confusing) data Tag a where
IsInt :: Tag Int
IsDouble :: Tag Double
someNum :: Num a => a
someNum = 3 * 4 + 5
pickNum :: Tag a -> a
pickNum IsInt = someNum
pickNum IsDouble = someNum * 2.5
Constraints can be delayed and changed by pattern matching, so the origin might not be obvious. There are ways around this, but they require tracking why values are set and to propagate this information around. Haskell has Turing complete type level functions so you have to figure out how they propagate this dependency information.GHC handles some easy cases, if you compiler in a file it does point to `+`, but it can get hairy. Also, currying can make it harder to say 'you swapped to arguments'