[Edit 2018-03-21: As you can read from replies below, the problems with syntax errors demonstrated by the quiz are already being worked on. However, Quiz 3 will remain valid.]
Hello nimmers!
Below you'll find a nim program that contains syntax errors.
Quiz 1: How many errors can you detect just by looking at the code?
Bonus Question: One of lines crashes the compiler (as of 0.18). Can you detect it?
type MyTuple = tuple(a: int, b: int)
var myTuple: MyTuple = [a: 10, b: 10]
type MyObject = ref Object
a: int
var myObject: MyObject = MyObject(a = 5)
type MySpecialObject = ref object of MyObject
var seqOfStrings: seq[int] = @[int]
proc silentProc()* = discard
proc inc(var a: int) = a += 1
Quiz 2: Now, imagine that you are a nim newbie. Try to fix the errors with the help of nim manual and compiler error messages. Or,better yet, ask a nim beginner to fix the code and watch the frustration grow.
The errors in the code were not invented by hand, they are real mistakes I admit I have made when learning to nim. Therefore I expect that other beginners will make the same mistakes. The point is that the compiler error messages for these examples are not only unhelpful, they are misleading - if you blindly follow them, you'll end up with even more confusion.
As someone who is currently heavily investing in nim, I hope that the user base will grow and the language will prosper. I see development of better syntax error messages as one of the high priority tasks in order to make nim more beginner-friendly. I'm namely worried that scary and misleading error messages will intimidate some newbies away from nim before they start to see the light. But the current semantically oriented error messages are very helpful for more seasoned programmers, so let's not loose them. Instead, I propose that there should be a separate syntax error detection phase in compiler pipeline that could identify common syntax errors and give helpful quick fix suggestions.
Quiz 3: Do you know other "easily made" syntax errors that produce misleading error messages? Help nim newbies by publishing your cautionary example here (or somewhere).
Happy nimming!
Bonus Question: One of lines crashes the compiler (as of 0.18). Can you detect it?
My bet is var myObject: MyObject = MyObject(a = 5), although it could actually be a lot of the lines. You have covered different permutations of invalid code quite well.
I see development of better syntax error messages as one of the high priority tasks in order to make nim more beginner-friendly. I'm namely worried that scary and misleading error messages will intimidate some newbies away from nim before they start to see the light.
I totally agree. But I don't think there is a high chance of serious effort being put into it before 1.0 is released. I hope to push a serious documentation and usability improvement initiative once 1.0 is released, it will be one of the major things that we will work on in versions post-1.0.
Some things are easier to fix than others of course. Crashes in particular should be relatively easy to fix. Making errors better is a bit more involved. But if you do find a crash please at least make sure you report it on GitHub.
Do you know other "easily made" syntax errors that produce misleading error messages? Help nim newbies by publishing your cautionary example here (or somewhere).
I don't off the top of my head. But thank you asking people to share theirs. It's easy for us to become accustomed to writing code in always the same way, and we don't have the time to fuzz the compiler to find strange crashes when code is written incorrectly. We definitely rely on newcomers to report these issues so we can fix them.
Quiz 2: Now, imagine that you are a nim newbie. Try to fix the errors with the help of nim manual and compiler error messages. Or,better yet, ask a nim beginner to fix the code and watch the frustration grow.
Or better yet, you ask a beginner who can read a tutorial again to look at some valid syntax. I'm not a fan of pathos.
The errors in the code were not invented by hand, they are real mistakes I admit I have made when learning to nim. Therefore I expect that other beginners will make the same mistakes. The point is that the compiler error messages for these examples are not only unhelpful, they are misleading - if you blindly follow them, you'll end up with even more confusion.
A compiler is not some interactive programming language teacher. That's certainly where the future is heading, but currently that is not what a compiler is. The compiler's error messages are about pointing out errors. Not about guessing what the beginner might have meant instead. For example, in [a: 10, b: 10] that is a valid array construction, but a is an undeclared identifier. The compiler doesn't look at the larger context to see that you use the wrong syntax for tuple creation.
Having said that, the compiler's error message are constantly improving. "Imagine somebody trying all sort of token permutations to create tuples" wasn't on my radar, but probably for the next version we can improve some of these error messages.
To drive my point home, I shall now reveal what the compiler tells the errors are. And boy, they are bogus.
type MyTuple = tuple(a: int, b: int)
# syntax...Error: invalid indentation
var myTuple: MyTuple = [a: 10, b: 10]
#syntax...Error: undeclared identifier: 'a'
type MyObject = ref Object
a: int
# syntax...Error: undeclared identifier: 'a'
var myObject: MyObject = MyObject(a = 5)
# syntax...Error: invalid expression: 'a = 5'
type MySpecialObject = ref object of MyObject
# syntax...Error: inheritance only works with non-final objects
var seqOfStrings: seq[int] = @[int]
# syntax...Error: internal error: expr(skType); unknown symbol
proc silentProc()* = discard
# syntax...Error: invalid indentation
proc inc(var a: int) = a += 1
# syntax...Error: ')' expected
The issue comes down to 3 problems for a nim novice:
Because there is no 1:1 correspondence between syntax errors and their semantic consequences, this cannot be simply fixed by search/replacing existing error messages. This also should/could not be fixed by complicating the parser (but tinkering nimsuggest might be ok, I guess), because looking at the larger context is not it's job. That's why I proposed a separate "common syntax error pattern detection tool". I agree that this not something that the core devs should be doing at the moment, but this would be a nice project for some wannabe core dev out there. (And, eventually, a quickfix on|off compiler switch would be nice).
Fortunately detecting all random permutations is not necessary; I believe that giving valid quick fix suggestions for the most common 20 syntax errors would keep 80% of the beginners happy.
Let me propose a simple short term fix: When the error message is not to be trusted, add a question mark to the error message. Instead of stately asserting "Invalid intendation", the compiler should politely suggest: "Invalid indentation?". This would save our beginner from spending his evening trying to fix the error by prefixing/removing leading whitespace (been there, done that).
But if you do find a crash please at least make sure you report it on GitHub.
Did that. Yesterday my first forum post, today my first issue, tomorrow the world? When it comes to nim, I guess I'm now all in.
the compiler's error message are constantly improving
True. To be honest, most of my beginner syntax errors I have collected long ago produce valid error messages now as of 0.18. For example, one of my pain points was confusing the usage of : and = operators. That the compiler now gives relevant feedback on this confusion is to be highly appreciated by nim beginners.
Quiz 4, strictly off-topic: try to guess the output :)
for x in [0..100]: echo x
Ok, correct answer to the quiz is: 7
You'll find the corrected code below.
As any decent quiz should, one trick question was included: although "inheritance only works with non-final objects" is reported as syntax error, there's no syntax error at that line; MyObject just should have been declared as "of RootObj".
type MyTuple = tuple[a: int, b: int]
var myTuple: MyTuple = (a: 10, b: 10)
type MyObject = ref object of RootObj
a: int
var myObject: MyObject = MyObject(a: 5)
type MySpecialObject = ref object of MyObject
var seqOfStrings: seq[int] = @[]
proc silentProc*() = discard
proc inc(a: var int) = a += 1
Btw1. This discussion is also relevant: https://forum.nim-lang.org/t/3647 It's a fact that when the parser gets carried away, it may state some irrelevant line as the source of your error. (Due to what StasB stated above).
Again, use the error message line number as a hint, not as a fact.
Btw2. Below is an example of a program that produces - while being a pedantically correct error message - quite scary output for a newcomer. A friendlier (and admittedly unrealistic to achieve) alternative would be something like: The & operator concatenates two strings, but the procedure debug does not return a string. Did you mean: debug("foo" & "bar")
import logging
debug("foo") & "bar"
The lesson to be learned: Just take nim error messages with a grain of salt (at least until Nim 1.x). After all, nim is more programmer-friendly language than it's error messages might first suggest.
"Error: Not all cases are covered" when you have one ')' too much? "Invalid indentation" when you have one ')' too much?
Your "quiz" has quite some potential still. :D
I'm also a beginner and I'm also exasperated by error messages just like you. Let's take C#, it's a language that was designed on purpose to be beginner friendly, and the compiler outputs messages that are incredible and spot on. This makes it frustration-free, and that's one key to adoption.
nim has a steep entry slope, I'm climbing it slowly but it's not without frequent requestioning "should I just scrap my proto and move to C# and redo it all in 2 days ?"
@Araq your attitude doesn't help with this feeling I'm afraid :'( you often are rough with your tone, and stuff like "a beginner who can read a tutorial again" that's... hmm
Ok, so the last way too long-lasting head scratching I had (I'll start collecting them like you @Allin lol) was when using ".items" and ".keys" iterators, it gives out weird errors about fields. I mentioned it in this SO question https://stackoverflow.com/q/49407160/893406. I'm trying to flood SO with everything I can ask that meets the SO standards. Because right now, google returns few useful stuff for common beginners queries, or the compiler's error messages
Because that's what we do, we copy paste a compiler's complaint in google and see if we're not faced with a common mistake, that is solved by checking the first answer on SO as first result in the google response. I've been out of misery in 10 seconds like that for lots of things. Never with nim. I hope this improves.
Unfair quoting IMO, I said
Or better yet, you ask a beginner who can read a tutorial again to look at some valid syntax. I'm not a fan of pathos.
A friendlier tone would have produced a friendlier answer. But I'm changing this to "just always be nice anyway", trying to listen to complaints. Independent of the tone I tried my best to improve the situation.
Because that's what we do, we copy paste a compiler's complaint in google and see if we're not faced with a common mistake, that is solved by checking the first answer on SO as first result in the google response. I've been out of misery in 10 seconds like that for lots of things. Never with nim.
"Nim is not Google friendly" is definitely Nim's problem number one for learners, agreed.
Hey I have another good one in the category "ignore the compiler and just go back to the code".
import db_mysql, tables
type XchgSym = tuple[xchg: string, sym: string]
var xchg_symbol2id = initTable[XchgSym, int]()
# real code:
#let id = db.tryInsertId(sql(qs), m.xchg, m.sym)
# simulation of the result for a stateless environment:
type RetTofTryInsert = type(tryInsertId(open("", "", "", ""), sql""))
let id: RetTofTryInsert = 1
xchg_symbol2id[(xchg:"", sym:"")] = id
change the last line to:
xchg_symbol2id[(xchg:"", sym:"")] = int(id)
and now it builds.
the compiler output is unhelpful:
comp.nim(11, 15) Error: type mismatch: got (Table[comp.XchgSym, system.int], tuple[xchg: string, sym: string], RetTofTryInsert)
but expected one of:
proc `[]=`(s: var string; x: Slice[int]; b: string)
proc `[]=`[T](s: var seq[T]; x: Slice[int]; b: openArray[T])
proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: S)
proc `[]=`[Idx, T](a: var array[Idx, T]; x: Slice[Idx]; b: openArray[T])
proc `[]=`[Idx, T](a: var array[Idx, T]; x: Slice[int]; b: openArray[T])
proc `[]=`[A](t: var CountTable[A]; key: A; val: int)
proc `[]=`[A](t: CountTableRef[A]; key: A; val: int)
proc `[]=`[A, B](t: TableRef[A, B]; key: A; val: B)
proc `[]=`[A, B](t: var Table[A, B]; key: A; val: B)
proc `[]=`[A, B](t: OrderedTableRef[A, B]; key: A; val: B)
proc `[]=`[A, B](t: var OrderedTable[A, B]; key: A; val: B)
Because the type it expects, is in fact not listed here. otherwise we'd have my tuple showing up.
The red tilda in VS code (and the column number in the compiler output) points to the first [ bracket of the key.
The only clue that got me to save the day, was that it was not showing RetTofTryInsert but i64, (I introduced RetTofTryInsert just for the sake of the argument). In fact the problem was on the other side of the assignment. Because it's NOT an assignment, it's an []= operator and that is very surprising. I have no i64 in my types, so changing to int was the fix.
I may be weird, but I find the above error message perfectly clear. Let me show how to read it.
It says it has various overloads for []=, I don't care for their type, but they all seem to take a t, a key and a val. Now the type mismatch happens between the provided values, which the compiler says have the following types
A rapid comparison shows that RetTofTryInsert is the wrong type here.
Unlike with some syntax errors, which may sometimes be unclear, when the compiler gives a type error, it usually tells you everything you need to know to figure out your issue.
not cool compiler message: https://imgur.com/a/AcANV
it'd be way better: "not nil only applies to ref types" maybe
we should have a pinned threads about those.