You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(defun sum (xs)
(case xs
[nil 0]
[(:: x xs) (+ x (sum xs))]))
(example
(the (-> (List Integer) Integer) sum))
The program type-checks but always returns zero, because the pattern for matching on the empty list is (nil), not nil. nil instead binds the list to a new identifier, nil, which shadows the (nil) data constructor.
I think this is very error-prone, not just for case, but in general: in a language in which a ton of custom macros use whatever syntax they want, it is easy to assume one syntax when the macro expects something else. For most of those mistakes, either the macro's parser will tell me that what I wrote is ill-formed, or the type-checker will tell me that what I wrote is ill-typed. But for this particular mistake, any identifier is well-formed and (if it isn't used in the body) well-typed, so the mistake is both common and not easily caught.
Thus, I propose to make most identifiers ill-formed, by using the convention (bind x) and the shorthand $x wherever identifiers are bound. For example:
I know this is quite a sharp departure from traditional Scheme/Racket syntax. But remember, at the beginning of the project we were willing to experiment with bold syntactic choices, in order to improve over that conventional syntax! We tried using [] vs () to distinguishing between the places in a macro's syntax where an unlimited number of arguments is allowed, vs the places where a fixed number of arguments is allowed, e.g. [let ([x 0] [y 1]) [+ x y]]. We ended up deciding this was a mistake. I think we still have room for more experiments and mistakes :)
The text was updated successfully, but these errors were encountered:
Can you spot the bug in the following code?
The program type-checks but always returns zero, because the pattern for matching on the empty list is
(nil)
, notnil
.nil
instead binds the list to a new identifier,nil
, which shadows the(nil)
data constructor.I think this is very error-prone, not just for
case
, but in general: in a language in which a ton of custom macros use whatever syntax they want, it is easy to assume one syntax when the macro expects something else. For most of those mistakes, either the macro's parser will tell me that what I wrote is ill-formed, or the type-checker will tell me that what I wrote is ill-typed. But for this particular mistake, any identifier is well-formed and (if it isn't used in the body) well-typed, so the mistake is both common and not easily caught.Thus, I propose to make most identifiers ill-formed, by using the convention
(bind x)
and the shorthand$x
wherever identifiers are bound. For example:I know this is quite a sharp departure from traditional Scheme/Racket syntax. But remember, at the beginning of the project we were willing to experiment with bold syntactic choices, in order to improve over that conventional syntax! We tried using
[]
vs()
to distinguishing between the places in a macro's syntax where an unlimited number of arguments is allowed, vs the places where a fixed number of arguments is allowed, e.g.[let ([x 0] [y 1]) [+ x y]]
. We ended up deciding this was a mistake. I think we still have room for more experiments and mistakes :)The text was updated successfully, but these errors were encountered: