Cond-expand in Racket

Hi,

i have some code, not written by me, i want to port to Racket and it uses cond-expand (see SRFI 0) to adapt to each scheme implementation,for example Chicken or Guile had a feature guile that exists when guile compiler is running, does Racket has something like that?

example with Guile scheme:

scheme@(guile-user)> (cond-expand (guile "Guile"))
$1 = "Guile"

Best regards,

1 Like

I don't know of a stand-alone implementation of SRFI 0 for Racket. If you are willing to accept the package dependency, I think the best off-the-shelf option would be the cond-expand from R7RS, which you can access using (require (only-in r7rs cond-expand)). For example:

#lang racket/base
(require (only-in r7rs cond-expand))
(cond-expand
 [(not (not (or (and racket) bogus)))
  #t]
 [else
  does-not-compile])

The R7RS implementation recognizes the following features as supported:

If you need other features, or if you want to avoid the dependency, it would also be reasonable to write a minimal cond-expand implementation specific to your needs.

You might also be interested in the convention of putting implementation-specific code in files with the extension .mzscheme.ss or .mzscheme.sls.

1 Like

That seems good. It is R7RS scheme, so portable.I even did not know there was this library in Racket, should be put in the manual page in my opinion or even proposed as language in the GUI.

Of course this is strange because i have to import a Racket R7RS package in code before testing i'm in Racket ! or in other scheme . cond-expand should be ,by default in Racket.The fact seems to be the code i'm porting try to define module for Guile,Chicken in the same file using cond-expand ! This is a bit too much. It should have be more 'readable' to have different scheme implementation for each scheme dialect.

But with this solution i could keep the code of all implementation in the same file, that's interesting if i go back later on this code to test it on a different scheme implementation.

About the SRFI 0 i try to put the source code, but i can not get a result, i do not see where the conditional is in the code? :thinking: If i try to detect for example a special racket feature such as current-namespace or version it does not worked with SRFI 0 source code.

Thank you.

The sample implementation in SRFI 0 “assumes that all existent features are built-in (here we assume srfi-0 and srfi-5 are present)”. The only conditional in the code is the pattern-matching of syntax-rules. The implementation handles and, or, and not forms by recursively expanding to cond-expand, so any supported feature clause will eventually match one of these cases:

    ((cond-expand (srfi-0 body ...) more-clauses ...)
       (begin body ...))
    ((cond-expand (srfi-5 body ...) more-clauses ...)
       (begin body ...))

If there is no supported feature clause, the cond-expand form will instead reduce to one of these cases:

    ((cond-expand) (syntax-error "Unfulfilled cond-expand"))
    ((cond-expand (else body ...))
     (begin body ...))

This is obviously a very awkward way to implement cond-expand, but, at the time of SRFI 0, syntax-rules was the only portable way to write macros. The perspective of the SRFI system, especially in early SRFIs, is that a SRFI is a request for implementation, with the sample implementation being truly just a sample. Scheme implementers are requested to implement the interface using their implementation's specific features. SRFI 0 is a particular example of a case when distributing the sample implementation unmodified would be essentially useless.

One of the curious aspects of SRFI 0 is that it doesn't define a registry of feature symbols. I think other SRFIs may have done so, at least to a limited extent. The list recognized by Alexis’ R7RS Small implementation comes from R7RS Small Appendix B, but that appendix explicitly does not require that cond-expand recognize all of the feature identifiers corresponding to provided features (it merely forbids recognition of any feature identifier for a feature that is not provided), and Alexis’ implementation does not seem to try to be comprehensive, especially when it comes to the open-ended sets of feature identifiers.

I don't know of any specification that defines current-namespace or version as cond-expand features.

Overall, I do not recommend cond-expand as a way of structuring portable Scheme, and I recommend it even less as a way of structuring Racket. I would approach it as a construct you need to stub out so you can port code that uses it, not as a construct for making code more portable.

1 Like

Hello Philip and thank you for your long answer,
i better understand 'cond-expand' now enough to write a version that match with Racket even if i'm not sure to understand the scheme macros, because often when i used them it seems i have still something to learn about them.

I could add a clause in cond-expand like that for racket:

 ((cond-expand (racket body ...) more-clauses ...)
       (begin body ...))

so the whole 'cond-expand' of srfi-0 would now be for racket:

(define-syntax cond-expand
  (syntax-rules (and or not else srfi-0 srfi-5)
    ((cond-expand) (syntax-error "Unfulfilled cond-expand"))
    ((cond-expand (else body ...))
     (begin body ...))
    ((cond-expand ((and) body ...) more-clauses ...)
     (begin body ...))
    ((cond-expand ((and req1 req2 ...) body ...) more-clauses ...)
     (cond-expand
       (req1
         (cond-expand
           ((and req2 ...) body ...)
           more-clauses ...))
       more-clauses ...))
    ((cond-expand ((or) body ...) more-clauses ...)
     (cond-expand more-clauses ...))
    ((cond-expand ((or req1 req2 ...) body ...) more-clauses ...)
     (cond-expand
       (req1
        (begin body ...))
       (else
        (cond-expand
           ((or req2 ...) body ...)
           more-clauses ...))))
    ((cond-expand ((not req) body ...) more-clauses ...)
     (cond-expand
       (req
         (cond-expand more-clauses ...))
       (else body ...)))
    ((cond-expand (srfi-0 body ...) more-clauses ...)
       (begin body ...))
    ((cond-expand (srfi-5 body ...) more-clauses ...)
       (begin body ...))
    ((cond-expand (racket body ...) more-clauses ...)
       (begin body ...))
    ((cond-expand (feature-id body ...) more-clauses ...)
       (cond-expand more-clauses ...))))


and running it like that:

(cond-expand
 (racket "Racket")
 (else "je sais pas"))

would return the correct result:

"Racket"

Perheaps i will use this modified 'cond-expand' but anyway, as i must be from the start already in a Racket module it is like when the Dupont and Dupond say "Et je dirais mĂŞme plus ... c'est du Racket."

You're right, 'cond-expand' is not the best way to make code portable, but it is the way this third party code is written (not by me).

Damien

but then it would not works with another scheme implementation,

forget my last post, :sweat_smile: my solution is wrong... still i have to learn about macros

Regarding identifiers for implementations and "features", there is https://registry.scheme.org/

1 Like

A paradox of cond-expand is that it cannot be portably implemented using syntax-rules, because the supported feature symbols—which are inherently implementation-specific—have to be hard-coded into the syntax-rules patterns.

Basically, you need to use some other portability technique to arrange for a suitable definition of cond-expand to be present for each Scheme system. That's one way this might help:

(If you have procedural macros, which were not standardized when SRFI 0 was written, it is more feasable to write a portable cond-expand, but my recommendation is to just do whatever is easiest for porting the concrete code at hand.)

Yes ,I understand a bit all that but the solution you provides is already good and used in the code now.
I'm trying to port to Racket the SRFI 110 implementation, not really for sweet expressions, i dislike that, but because the code has been written one year after the one of SRFI 105 and it integrates a perheaps better curly infix parser. As i already fixed things in SRFI 105 previous code i find interesting the SRFI 110 code that implements both,sweet,curly and neoteric expressions in the same code in a more modern and complete way perheaps (to be checked and tested).

See: https://srfi.schemers.org/srfi-110/kernel.scm
there is even a latest version of kernel.scm in this archive:
Download readable-1.0.10.tar.gz (Readable Lisp S-expressions)

you can then see the use of 'cond-expand' i working on, of course i can get rid of all that,and anyway in modern code i started to put all this code in a Racket module, so there is a bit of non-sense to keep the 'cond-expand' in a racket module, i know that :person_shrugging: . Perheaps the only interest of 'cond-expand' is if later the modified code has to be ported back to Guile (i think readable had been made for Guile first) or to another scheme such Chicken as i see a few support for this scheme for this implementation too.I already have an old version of SRFI 105 for Scheme+ for Guile that i should upgrade one day.

How is one supposed to use these identifiers in a program?

-- hendrik