Tip:
Highlight text to annotate it
X
Alright in this segment I want to take a brief break from building up our
conceptual framework for understanding ML to go through a couple more pragmatic
topics namely how to use the REPL, that Read Eval Print Loop effectively as well
as how to deal with error messages and show you a bunch of examples of errors
that are fairly common when starting to learn ml. Okay. So we wanna ask a couple
questions. First of all is how do we run programs using the RPL and what does it
mean to have a REPL? And then what happens when we make mistakes and how can we go
about debugging those mistakes? And fundamentally, debugging is, is a skill
you develop over time, and I just want to give you some practice with it by watching
me do some. So, the way we've been using our repple, was primarily with this use
expression, and really the way that I want you to think about it, is that it takes
the contents of the file, and it's like you typed in those bindings, those
variable bindings, one at a time into the REPL. And that really is what happens. So
if I take this file which we've seen before, it has a bunch of bindings. When I
come over here with Control C, Control S, Return to bring this up. I could manually
type these in one at a time, val x equals 34, val y equals seventeen and so on. But
instead, when I say use first dot sml, it does that but all at once in a batch
showing me the result of doing all of those bindings. What were their types and
what's the result of evaluating them. And that's all that it's doing, and it's
convenient that way. And so when you look at it this way, what is actually going on
is the meaning of our programs is that all we have is a REPL. And use is just a
convenient way to use that REPL. So, REPL stands for Read Eval Print Loop and it
actually pretty well named. But what happens at that prompt, when you type in a
binding, is that it reads it. It evaluates it, although if it doesn't type check, it
just gives you an error message to that extent. It prints the result, and then the
loop part is about how it gives you another prompt back so that you can
continue. So we can just think of this as a strange way to run programs. Where we
open up the repple and then we type use. But it's also quite convenient for after
you type use, to then go ahead and try a few things out. Now after you types Us you
find yourself typing the same thing over and over again. Like you're building up a
collection of test cases for say your homework problems. You're probably not
making an efficient use of your time. And it's better to go ahead and move those
tests into a second file, and then go ahead and use one file, then use a second
file that's going to include all your tests. And then maybe do a little extra
playing around. But what I don't ever recommend, for reasons we'll discuss in
the next segment, is typing use, playing around and then using the same file again,
instead, what you always see me do is come over here to the repple, type control D to
end my session, control C, control S return to restart it and then it's okay to
type use again with the file. The reppa wont stop you from doing it another way,
but I don't recommend it because it's often very confsing what's going on. Okay.
So now let's turn to errors. usually when I show you programs here in the video,
they just work in trying to demonstrate something. But we all make mistake. You
can make a syntatic mistake, you don't write something down the way you expected.
You can make a type checking mistake, what you wrote down is the correct syntax, but
it doesn't follow the type checking rules for the language console you're using. Or,
it might type check and then run. And either, it, it produces an exception, or
goes into an infinite loop, or it produces an answer, a value, but not the value you
wanted. So debugging has to attack all of these problems, and one of the hard things
is you often don't know which mistake you made, and the error message you get from
ML may not help either. And there's a few reasons for that. Well the, the good
reason is, well you wrote something that isn't what you intended. So whatever error
message you get is just ML's best guess as to what might be the problem. But it's up
to you to figure out what actually the problem is. And the other thing is, is
that as much as I love ML as a language, and love having a read of val print loop,
the error messages you get particularly when you have a type checking error, are
usually actually quite bad. And, it's really a bit of an aquired skill to look
behind them and figure out what you did wrong. So the best way to learn something
is by trying it and I want you to just try writing in all programs. Don't be afraid
of errors, slow down, find what line number the error's coming from and so
forth. And I thought I would help point you in the right direction by just doing a
little bit of that myself. So I have a second file here that is called errors.sml
and it looks like a pretty good program here. It turns out it has a bunch of
errors and this is the entire thing but suppose I wrote all this down. And now I
go over here and I say, use errors dot sml, 'kay? So it turns out, it gives me a
couple errors here. The first one you can see is at line fourteen and it says,
syntax error inserting else ID. Okay? So if I go over to line fourteen. Which you
can see here in emacs down at the bottom. It prints out the line number. That line
is just val a = -five. So it's not, you can't look at that line, and figure out
why it's inserting an else. It turns out, is often ca-, the case. Well, the error is
actually a little bit before the line number that's being reported as the
problem. And indeed if you look up here I have this if then expression. Well, in ML,
there's no such thing as an if/then expression. You need an else afterwards,
which it thought you might wanna put on line fourteen. I actually want to put it
up here. And I have to have else something. How bout else 42? And that's
because, as we know, when you have a conditional expression, either branch, the
then or the else might need to be evaluated. So you can't leave the else off
li ke you can in some programming languages. So, okay. We fixed that error.
Let's go back here. Come down to the end. Restart things. Try again. And by the way,
if you don't want to keep typing this, you can type meta p to get your previous
commands back. And now we see an error on line eighteen. It says replacing fun, with
wild. Now if you look at line eighteen here, it might look just fine to you. Why
can't I have a variable called fun? Well, as often happens when you're learning a
programming language, we stumbled across a keyword. It turns out fun means something
else in the language and we're not allowed to use it for a variable. so we're gonna
have to change this and how about funny. That should do the trick. Quit. Restart.
type that back in. And now, we get a whole bunch of error messages. They don't even
fit on the screen. Because we're now, don't have any syntax errors that ML
found. So it's giving a bunch of different type errors. And I like to fix the first
one and then come back. So, up here at line eight, it says, unbound variable or
constructor x. So if I look at line eight. Sure enough, I am using x right here.
yx+1. equals x + one. But it sure looks like it should be bound.
It should be in my dynamic environment from the previous line. But it turns out
that the error here actually is syntax, even though it looks like type checking
because I'm not actually finished with the previous expression. If you wanna start
another expression, you have to type val again. Since I left that off the type
checker thought this entire thing was one expression and for this expression, there
is no x in the dynamic environment yet because I haven't finished defining it. so
I have to put val here. By the way, I used to put semicolons at the end here. And you
still can. You have to in the REPL. But in a file, it's not actually necessary. So it
turns out that was not the error. But leaving off the val was. So, alright? So
we'll try this again. And we still have a bunch more errors. But at least now, we're
up to line ten. Where it say s, test expression in if is
not of type bool. If we go over here to line ten, a ha, it actually got one right.
This is actually very accurate error message. Right here, this expression Y,
has type ent, because that's the type of the variable Y, and you're not allowed to
have something of type int there. You have to have something of type bool. So, this
is just a nonsense code, but maybe the programmer meant to write, if Y is greater
is zero. Okay, so we fixed that one. Come back here, restart. Try it again. Still a
bunch of errors. Still on line ten. Types of if branches do not agree. we
remember that the then branch and the else branch both have to have the same type. So
that, that can be the type of the entire if/then/else expression. And, indeed, 34
is type int. And x less than four has type bool. So I don't know which of these is
wrong, but I have to fix one of them. How about fixing that one? Alright, back we
go. I hope we don't usually make this many errors in this few lines of code but we're
up to line fourteen. Expression or pattern begins with in fix
identifier minus. This one might not make you very happy at all. It's actually
complaining about that minus five and maybe that seems crazy to you but in ML
the only thing you can use minus for is as a binary operation like X-5 or 0-5 or 13-5
or so on. If you want to write a negative number you might well say 0-5 will work.
you don't have to do it that way. there's just different syntax in ML. When you
learn a programming language, you have to learn that language's syntax rules and it
turns out that negation of one argument is the tilde character, with or without a
space here not the minus character. You can't use minus. You have to use tilde.
And that will fix this problem. and, we're up to line twenty. Operator and operand
don't agree, real star real, int star int. The expression it's complaining about is
this X slash W, where presumably I was trying to do an add division. Well, that
is how you do division on floating point numbers in ML which it calls reals, bu t
it doesn't apply to integers. You're not allowed to use slash on integers in ML.
That's just the type checking rule, but there is a different operator for division
and it's spelled D, I, V, for div. Alright, so we maybe look that up
somewhere. and we got that fixed and now we can run this and. Ha, that looks like a
very different kind of message. So it turns out we're not getting a type
checking error here at all. We're now actually, everything type checked and
we're running the program. But when we ran it we got an uncaught exception for divide
by zero. Like in many languages you get an error if you divide by zero when you run
the program. It's not a typing error and sure enough if you look at this X div W,
you go to evaluate W, we look it up in the dynamic environment and we get zero. So if
we don't want that error we're going to have to use some different addition
division. How about dividing by W+1? So let's go back here one more time. And look
at that. We actually ran. We have a bunch of types now, we have a bunch of values.
But if you look at the N, not this val it. That's the result of the use itself. But
the last variable by name we did. I created a variable fourteen, and it is
bound to the value zero. Of type int. Now that's not necessarily a problem, but
maybe it's not what I meant. And I brought this up, I included this just to remind
you that sometimes you get this, great sense of satisfaction when it all type
checks. It all runs, but you still have to make sure you have the right answer, and
if I wanted this to be fourteen, I probably didn't want seven minus seven. I
wanted seven plus seven. Of course, there's no way a type checker or an
automatic tool can know what I meant. Fourteen is just an English word, but you
still have to test your program, and, and look at the answers and make sure it all
works. And in this case, after fixing that. Finally happy with what I've got and
so let's stop there.