-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stepper: head/tail applied to non-pair crash the stepper #1720
Comments
It is legit for append to call tail if the argument structure is not null. The functions head and tail is designed to give a nice error if the argument is not a pair, and OP gives the implementation of tail. With a bit of programming experience using lists, the programmer quickly learns that the error "head/tail(xs) expects a pair as argument xs" results from using something that is not a list in a place where lists are expected. Of course the stepper must not crash, so that's a problem in the stepper that we need to fix. Minimal example to crash the stepper:
|
Regarding the question: Another issue is that the append function also does not check if the second parameter is a list, it simply replaces the null at the end of a list with the second parameter, thus append(list(1), 1) returns pair(1,1). Since this is being written in typescript, why isnt type checking done in the function definition in the first place? The reason is that the runtime for checking if a value is a list has an order of growth Theta(n) where n is the length of a list. Note that SICP JS is written using JavaScript, not TypeScript. Generally, we don't check the types of arguments in SICP JS for the sake of simplicity of the resulting programs. Yes, the append function will produce something that is not a list if the first argument is a list and the second argument is not a list. We accept this as an unintended consequence of the way append works. |
Sure with some experience with this language, it can be clear that somewhere in the code I am using list functions on non list data types, but that is not immediately obvious when someone first encounters the issue like I did yesterday. The first response is immediate confusion over why a random line not in the program is producing this issue, which is not ideal. Secondly even if a programmer knows that they are accidentally introducing non list values to a list function, it may not be clear which line is producing this error and why. This can be troublesome in large programs or when using recursive functions and some cases are not properly taken care of, the other bug that tail(1) crashes the stepper makes this even harder to figure out. I think that the append function should at least catch the error thrown by the tail function and throw it back so that the error message is more clear. Or it can check if the input is a number, which should be O(1). |
Also its not just programs that error like head(0) that causes the stepper to crash, I was playing with an iterative version of tree_sum which does not error and it still crashes the stepper with the same error message function tree_sum(tree) {
function iter(sub_tree, stack, sum) {
if (is_null(sub_tree)) {
if (is_null(stack)) {
return sum;
}
else {
return iter(head(stack), tail(stack), sum);
}
}
else {
if (is_number(sub_tree)) {
return iter(null, stack, sum + sub_tree);
}
else {
return iter(head(sub_tree), pair(tail(sub_tree), stack), sum);
}
}
}
return iter(null, tree, 0);
}
tree_sum(list(1)); I could investigate what exactly causes the error later on |
I agree with you wholeheartedly. I think starting with a typed language such as TypeScript would solve the problem. SICP JS follows SICP in its use of an untyped (better: dynamically typed) language. There we have a dilemma: Do we complicate all our functions to make sure they are applied only to data of the right type, or do we assume that the given arguments have the right type. We follow SICP by choosing the latter. |
Fixed by #1719 |
js-slang/docs/lib/list.js
Lines 240 to 248 in 56d8d6b
The append function does not type check if the input list is even a list, when I run the following line of code on source academy:
An error from the tail function is thrown
Line 121: Error: tail(xs) expects a pair as argument xs, but encountered 1
This is because in lists.ts,
js-slang/src/stdlib/list.ts
Lines 48 to 54 in 56d8d6b
tail checks if the input is a pair, since 1 is not a pair, an error is thrown. The append function should also check if the input is a list, and throw an error if not, to prevent confusion. Furthermore when the code is run in the stepper, it crashes.
Another issue is that the append function also does not check if the second parameter is a list, it simply replaces the null at the end of a list with the second parameter, thus append(list(1), 1) returns pair(1,1).
Since this is being written in typescript, why isnt type checking done in the function definition in the first place?
The text was updated successfully, but these errors were encountered: