Final SRFI 253: Data (Type-)Checking Arthur A. Gleckler (15 Nov 2024 23:12 UTC)
Re: Final SRFI 253: Data (Type-)Checking Jeremy Steward (16 Nov 2024 23:21 UTC)
Re: Final SRFI 253: Data (Type-)Checking Artyom Bologov (17 Nov 2024 14:51 UTC)
Re: Final SRFI 253: Data (Type-)Checking Jeremy Steward (17 Nov 2024 17:04 UTC)
Re: Final SRFI 253: Data (Type-)Checking Artyom Bologov (17 Nov 2024 19:20 UTC)

Re: Final SRFI 253: Data (Type-)Checking Jeremy Steward 16 Nov 2024 23:20 UTC

Hey Arthur, Artyom,

On 11/15/24 16:12, Arthur A. Gleckler wrote:
> Scheme Request for Implementation 253,
> "Data (Type-)Checking",
> by Artyom Bologov,
> has gone into *final* status.
>
> The document and an archive of the discussion are available at https://
> srfi.schemers.org/srfi-253/ <https://srfi.schemers.org/srfi-253/>.
>

Thanks for finalizing this! I've gone ahead and forked the sample
implementation and started packaging it for CHICKEN Scheme.

<https://gitlab.com/ThatGeoGuy/srfi-253/-/merge_requests/1/commits>

I mostly work on GitLab, so apologies for switching over there. I've
added CI to my version of the repo to be able to have some automated
testing for the CHICKEN implementation, which is my justification for
the fork across forges.

Nevertheless, there were some issues that I think I wanted to address
with the CHICKEN versions of some procedures in the sample
implementation, and one minor issue related to imports / exports. I'll
say upfront that these are all implementation problems that I addressed
in the version I'm packaging for CHICKEN Scheme, but don't represent any
actual issue with the SRFI as proposed.

1. Assume / assert
2. values-checked
3. check-case
4. Exports

# Assume / Assert

There is a form in the sample implementation:

(cond-expand
  (srfi-145)
  ((or r6rs chicken loko)
   (define-syntax assume
     (syntax-rules ()
       ((_ expr rest ...)
        (begin (assert expr) #t)))))
  (debug
   (define-syntax assume
     (syntax-rules ()
       ((_ expr rest ...)
        (or (and expr #t)
            (error "assumption violated" 'expr rest ...))))))
  (else (define-syntax assume
          (syntax-rules ()
            ((_ rest ...)
             (begin #t))))))

I know nothing of r6rs, loko, or what you aim to do with the debug
feature in this cond-expand; however, CHICKEN's assert procedure is
pretty much already what you want for `assume`.

<https://api.call-cc.org/5/doc/chicken/base/assert>

Notably, there's two things that I think need to be changed in the above
form for the sample implementation:

1. CHICKEN should include the `rest ...` args when calling assert.
2. The `(srfi-145)` part of that cond-expand should be lower priority.

I think the former I can pretty well summarize in this commit:

<https://gitlab.com/ThatGeoGuy/srfi-253/-/merge_requests/1/diffs?commit_id=a47ebe4b43d13010c5b29d38734a0c8c1b670071>

Basically, you want `(begin (assert expr rest ...) #t)`, so that the
remaining arguments give a better error message. The latter may be
controversial, but I'll point out that CHICKEN's implementation of
SRFI-145 basically wraps the sample implementation, which makes the same
mistake. I can understand the desire to reuse code, but SRFI-145 (at
least in CHICKEN) isn't an ideal place for this to be fixed.

Further, installing SRFI-145 as a dependency for SRFI 235 is a lot of
overhead. It requires pulling the package off of the internet, as well
as actual time to compile the whole egg on top of SRFI 235 itself. Given
how modules / compilation units in CHICKEN work it simply makes more
sense to just use `assert` here in place of SRFI-145.

As a final note: I have noticed that for some SRFIs there are members of
the CHICKEN community who wrap the sample implementations in a pretty
bare-bones way, or in a way that might lead to performance degradations
(due to omitting optimizations, or by wrapping existing procedures in
such a way that CHICKEN's optimizer cannot always make good decisions
across compilation units). It's an implementation problem, and one that
is easily avoided if you just redefine syntax like `assume` in the crate
itself.

# values-checked

In the sample implementation of values-checked, you're using CHICKEN's
`the` operator to specify a type-check. This is probably a
misunderstanding of how that part of CHICKEN works — `the` will not
actually emit an error in the way you want it to. It will emit a warning
if you compile with specializations on (-O3 and higher), but it doesn't
do anything in the interpreter (csi) or if you compile with
specializations off. Instead, it will just return the last value
provided to the `(the ... value)` form.

Given that the SRFI specifies that it is an error if you don't have a
match here, I opted to just remove the CHICKEN-specialized version and
use the version in the `else` block in that same cond-expand. This
produces a version which passes the tests.

# check-case

I removed the CHICKEN-specific implementation of check-case as well,
opting for the portable version. Unlike values-checked, the provided
version mostly works, but there is one particular issue surrounding the
use of compiler-typecase:

<https://api.call-cc.org/5/doc/chicken/types/compiler-typecase>

While I think it would probably be correct to use something like
compiler-typecase for check-case, the problem is that this specific API
behaves very differently when run by the interpreter (csi) or with
specializations off (-O2 and lower).

Basically: it needs an else-clause all the time. When this else clause
is omitted and this is run during compilation with specializations
turned on, CHICKEN will raise this as a compiler error and emit the
correct message to the user.

As it stands this API is hard to make generic for all cases (you might
for example compile the SRFI-253 egg with -O3, but downstream users can
compile their own code with -O2 and run into issues). I would love to
fix this and add an else-clause that does the right thing in the right
place, but I don't really understand the macro well enough to make that
change (I spent about ~2 hours on it and couldn't crack it, but others
are welcome to try). As it stands the CHICKEN-specific impl in the
sample implementation repository fails this test:

     (test-assert (check-case "hello" (string? #t)))

Because if you run this in the interpreter (tests in CHICKEN eggs are
run in the interpreter after compilation and installation of the
library) it will fail complaining that you don't have an else-clause
where you need one. Note that executing that check-case can be all well
and good, but that else-clause still needs to be there.

If there is a way to crack this with compiler-typecase, I'd be more than
happy to take a patch for this on the GitLab repo I've shared (or just
at my email).

# Exports / Imports

The correct list of exports for the sample implementation is:

(export check-arg
         values-checked
         check-case
         lambda-checked
         define-checked
         case-lambda-checked
         define-record-type-checked)

However, the sample implementation still tries to export
opt-lambda-checked and define-optionals-checked, and fails to export
check-case. You might want to fix this for other Scheme implementations.

Aside from that, good job! Many of the tests "just worked" and diving in
wasn't too bad for someone who is not a macro expert. I think most of
these issues come down to familiarity with CHICKEN and a sort of
chicken-egg problem where packaging the code as an egg helps diagnose
some of these issues when trying to use CHICKEN overall.

I don't know if I'll wait for any feedback on this message to merge the
above MR on GitLab and tell the chicken-users mailing list, but I am
definitely open to any help that can be provided here to make the
CHICKEN implementation better.

Thanks, and Regards,
--
Jeremy Steward