I need to clarify a couple of things.
fun
u8{i:uint8}
(i: int(i)): uint8(i) = cast{uint8(i)}(i)
I interpret this to mean:
for all i:uint8 I can pass in an int that is restricted to a value 0-255
and it returns a uint8 with the same 0-255 restriction.
Pretty nice actually.
Yes, the sort uint8 is defined as follows:
sortdef uint8 = {i:nat | i < 256}
But, when I call like this:
val value = 2: uint8
val _ = to_digit(u8_(value >> 4))
It can’t seem to check that a shifted value is within the constraint.
That just means to me the constraint solver does not handle shifts. So as
the caller to to_digit, I would need to certify that the value meets the
requirement with some kind of cast. Is there a general form of cast that
can include something like cast{i:uint8 | i < 16}(value >> 4)?
The type for ‘>>’ is not accurate enough to capture what you want here.
val value = 2
val value2 = value >> 4
val value3 = g1ofg0(value3)
val () = ckastloc_gintBtw(value3, 0, 16)
// at this point, the compiler knows 0 <= value3 < 16
As an alternative, I suppose I could create a function that does the
conversion, with either a run time check that aborts, or with some range
clamping, etc. But if I do that, all I did was move the original clamping,
the side effect of a pattern match on anything that returns 0, to another
place in the code.
In general, you want errors to be reported as early as possible. For
instance, we could return 0 (instead of raising an exception) if the index
used to access an integer array is
out-of-bounds, but this can complicate debugging so much!
I know there is no ultimate escape if bytes are given the program via USB
and they must be interpreted as hex, it is more a matter of where they are
checked, clamped, or cause an error to be returned by USB. But I would like
to know how you would handle this. Would you make a function to get from
byte to constrained hex byte, use a cast, etc.
I would try to identify and report errors as soon as possible.