Given that Beginning Student has support for signatures, how hard would it be to allow Image, Color, etc as types so that signatures could be used with image functions?
On Nov 11, 2024, at 10:45 AM, Todd O'Bryan via Racket Discourse asked “[g]iven that Beginning Student has support for signatures, 'whether it would hard] to allow Image, Color, etc as types so that signatures could be used with image functions.”
The answer is sadly simple:
— labor, lots of
— organizing an appropriate teachpack for inclusion with HtDP.
The current language is somewhat impoverished for the “image aspect” of our teaching world, but it comes with all the ingredients for getting this right.
(Having said this, I doubt I would use checked signatures in a first course of my design. This course is overloaded and having checked signatures distracts from the major teaching goal of “good program design demands mental discipline.” My guess is that this will become even more important when AI takes over the production of 20-50% of our code, because reading code and spotting problems requires even more of the mental discipline than programming in a type-driven, but unchecked context.)
I'm willing to provide a lot of labor if someone can point me in the correct direction.
I appreciate the idea that we encourage mental discipline, but Javascript is being slowly supplanted by Typescript and Python3's optional typing allows type-checking. I'm dealing with a lot of muddied thinking that doesn't get caught until I can grade student code, at which point it may be entrenched.
I have students typing contracts like:
;; my-function: size text color -> image
because they don't really understand what a "type" is. Being able to require
(: Number String Color -> Image)
and have an error appear if they try
(: Size Text Color -> Image)
would be a godsend for me. I mean, students only learn from feedback, and if the contracts are just comments, then the only feedback they're going to get is from me. I don't want them to have to wait that long.
Honestly, the type checking prior to running is icing on the cake. I'd he happy with contracts in code that weren't checked until runtime, but I need some way to allow students to self-check their understanding of types.
I think I could add types to 2htdp/image
as easily as
(define-type StringMode (U "solid" "outline"))
(define-type SymbolMode (U 'solid 'outline))
(define-type AlphaMode Byte)
(define-type Mode (U StringMode SymbolMode AlphaMode))
And then do something similar for YPlace
, XPlace
, etc., and add signatures for all the functions provided.
(define-type Color ...)
would be a bit of a pain, but as long as the type system doesn't mind unions with a lot of literal values, it shouldn't be a problem.
I'm not sure how you'd (define-type Image ...)
except that images are instances of bitmap%
or image-snip%
, so hopefully there's a way to declare a type as a union of classes or something.
At any rate, I'm happy to take a shot at this.
Checked signatures are not types.
You will need to read up on Mike S.’s signature docs and build base signatures from there. Then combine them and equip some of the functions in 2/image and 2/universe with such signatures.
I recommend putting it all in a separate file for now and telling students to require this file, after you have written solutions with these things to the problems you assign, We can work on turning this file into a teachpack later.
It's probably mostly a matter of copying & uppercasing the names here:
Thank you, Mike! I think I almost have it working the way I'd like. The issue is color
vs image-color
, where I'd prefer 'blue
, "blue"
, and (make-color 0 0 255)
to all satisfy the signature Color
, if possible.
Matthias mentioned that I should read up. Is there an article or something I could read?
In particular, can you explain what signature/arbitrary
does? I've changed
(define Mode (signature/arbitrary
(quickcheck:arbitrary-one-of string=? "solid" "outline")
mode (predicate mode?)))
the first Mode
to upper-case, but not the one in the last line. It seems to work, but I can't figure out what the mode
before (predicate mode?)
is doing.
It looks like if I have students use uppercase names for structs that I'll get the signatures for free. As in
(define-struct Student (name grade))
gives me Student
for signatures, Student?
as a predicate, and Student-name
and Student-grade
as selectors.
Is adding (: make-Student (String Number -> Student))
the right way to specify what types the fields are expected to be?
And two more questions:
- Is there an easy way to do signatures for unions? Those cases that HtDP used to define like:
;; A posn-to-draw is:
;; number (x-value, with y-value assumed to be 0), or
;; posn
- I added a signature for the
2htdp/image
circle
function, but it looks like the contract gets checked before the signature, so even if we added signatures for all the functions, it wouldn't make a difference in terms of what students would get as feedback. Even if I do this,
(: circle2 (Number Mode Color -> Image))
(define (circle2 r m c) (circle r m c))
and call (circle2 "solid" 20 "blue")
the error I get is from the contract "circle: expects a non negative real number as first argument, given "solid" "
, not the signature violation.
Any way to add signatures that will actually do anything?
@mike Not sure if you've seen this, but just wondering if you have any ideas.
When I tried to reproduce your results using BSL with simple numbers (Racket 8.15), I got:
- error from bad signature; and ALSO
- a testing error window explaining that there were a signature violations, listing line and value
That is, the signature violations are noted but execution is attempted anyway. That is definitely what you want for beginners.
The contract error probably stops the rest of the code from running, but the signature error won't.
Is your setup different from this?
Sample code:
(define (xerror a b)
-1)
(: f (Number -> Number))
(define (f x)
(if (number? x)
(* x x)
(error 'f "input should be a number"))) ;; change to xerror to observe signature violations do not stop later code from running
(f 5)
(f "Bad")
(f "Does not run")
(check-expect (f 5) 25)
(check-expect (f 10) 100)