Important! You must look over these examples carefully and make sure you understand them. They illustrate the difference between *static* and *dynamic*. Here that is the context of errors, but the point is to see that difference between phenomena occurring as the program is executed (dynamic) vs. occurring before execution (static).
Below are examples of the same static error in C/C++, Python and Java. We are missing an "=" when res is supposed to be assigned a value. If the program starts executing we should see the "in modadd!" message get printed before the error. We don't, which is how we know this is a static error.
| Static errors: these happen before the code is executed (note: the "in modadd!" message is not printed) | ||
$ g++ ex0.cpp
ex0.cpp: In function ‘int modadd(int, int, int)’:
ex0.cpp:6:11: error: expected initializer before ‘x’
6 | int res x + y % n;
| ^
ex0.cpp:7:10: error: ‘res’ was not declared in this scope
7 | return res;
| ^~~
$ ./a.out
bash: ./a.out: No such file or directory
|
$ python3 ex0.py
File "~/ex0.py", line 3
res x + y % n
^
SyntaxError: invalid syntax
|
$ javac Ex0.java
Ex0.java:4: error: ';' expected
int res x + y % n;
^
Ex0.java:4: error: not a statement
int res x + y % n;
^
2 errors
$ java Ex0
Error: Could not find or load main class Ex0
Caused by: java.lang.ClassNotFoundException: Ex0
|
Below are examples of the same dynamic (runtime) error in C/C++, Python and Java. We are dividing by zero with "% n", since parameter n is zero. If the program starts executing we should see the "in modadd!" message get printed before the error. We do see it, which is how we know this is a dynamic error.
| Dynamic/Runtime errors: these happen during code execution (note: the "in modadd!" message *is* printed) | ||
$ g++ ex1.cpp $ ./a.out in modadd! Floating point exception |
$ python3 ex1.py in modadd! Traceback (most recent call last): File "~/ex1.py", line 6, in |
$ java Ex1 in modadd! Exception in thread "main" java.lang.ArithmeticException: / by zero at Ex1.modadd(Ex1.java:4) at Ex1.main(Ex1.java:8) |
ACTIVITY
int x = 42;, we are binding the name x to
the value 42 (as an int).
Of course, over the whole program there may be many different
bindings of the name x to different values.
The scope rules for a language determine which binding
is in effect when a particular name is used.
TODO: Below are equivalent Python, C++ and Java programs. They all have the same error, namely that there is no binding for a variable named "y" in scope when we try to use "y" in the function foo. Test, by compiling and/or running the code, possibly adding print statements if need be, whether this is a static or dynamic error in each of these languages. How do you know?
Important! Do your testing first, then read the following!
C/C++ and Java are statically scoped languages (aka lexically scoped). The rule in those languages is to look at compile time for the binding that is in use based on the program text at and around the spot where the variable is referenced. So if, at compile time, no binding for the variable exists, it is an error!
Python has local variables, like we are used to,
which are statically scoped, and global variables
which are dynamically scoped. Local variables, by the way,
means variables that are bound to values within a function or
class definition. You don't explicitly declare variables in
python like you do in C/C++ or Java, of course. Instead, when
a variable is assigned a value within a function or class, a
binding (local to that function or class) is created.
This means, by the way, that if you want some global variable z to
be modified in a function you have a conundrum:
writing z = 42 creates a new local variable, so
the global variable z is unchanged. To get around this you
add the declaration global z to the function
before your first use of the name "z", which indicates that
you want to use the global binding for z rather than create a
new one. At any rate, here are the two big takeaways:
def foo(x):
return x + y
... is perfectly OK. It is only not OK if you try to call
foo without first defining a global binding for y.
def bar(x):
global count
count += 1
return x*x
for i in range(0,len(L)):
what is the type of L[i]????
Since L can contain anything, you cannot possibly know the
type of L[i] until you evaluate it and see.
Important! The main issue with *types* in a language is to figure out:
Each of these has a call of function foo that is illegal due to type errors. Determine/demonstrate by compiling and/or running these programs in which language this is a *static* error and in which language it is a *dynamic* error.
For the language(s) in which this is a static error, explain *why* the error is detected prior to executing the code. For the language(s) in which this is a dynamic error, explain *why* the error is not detected until we are in the middle of executing the code.
x * y to x + y, then try your
three tests all over again. For one of them there is now no
error ... can you explain that change?
x + y,
as python3 -i ex3.py and try the following
calls:
foo(34,22)
foo(34,True)
foo("abc","xy")
foo("abc",3)
foo([3,4],[5,6])
foo({3,4},{5,6})
Important! Can you explain what's
happening here? What determines which ones are errors and
which are not?
TODO: Create a file with the following two functions, which both do the same thing: they print out an input list of strings, one per line.
Try running both versions on the following inputs:
printOnLinesVx(["red","fish","blue","fish"])
printOnLinesVx(["red",42,"blue",(4,5)])
printOnLinesVx({"red","fish","blue","fish"})
printOnLinesVx({"foo":"red","bar":"fish","rat":"blue","dog":"fish"})
printOnLinesVx("absolutely")
printOnLinesVx(42)
Important Questions!Discussion: What you should have seen is that you might write a function with a list in mind but, in fact, what matters is what operations/functions/methods you relied on. Anything else that has those same operations/functions/methods defined for it can be passed into the function and it will still work ... at least in the sense of not having type errors. The Python community has a cute name for this behavior: duck typing. This comes from the saying "if it swims like a duck and quacks like a duck then it is a duck". So if you wrote your function with a list in mind and used len(·) and subscripting with [ ]s, you might say "if it works with len(·) like a list and is subscriptable with [ ]s like a list then it is a list." Thus, as far as your function is concerned, a string is a list! Or a duck ... I'm getting confused.
doit(L) defined below:
TODO: The doit() function was designed for lists of strings, and it does what it's supposed to do when that's what it is given. Rewrite doit() to work more generally, so that all of the following calls work properly:
>>> doit(["foo","bar","rat","dog"]) foo bar rat dog >>> doit("NAVY") N A V Y >>> doit(["NAVY","ARMY",42,(3,4)]) NAVY ARMY 42 (3, 4) >>> doit({"foo","bar","rat","dog"}) bar dog foo rat >>> doit(range(1,5)) 1 2 3 4Important! Can you explain how it works on the last one!?!?!?