Parsing differences between Guile's SRFI-37 and getopt_long

2025-06-10

For a long time now I was getting irritated every time I tried to bind some directory into a Guix's shell container. The usual --expose ~/foo does not work. You have to do --expose=$HOME/foo due to a peculiarity of args-fold (from SRFI-37) distributed with GNU Guile. Before I write my own implementation, I want to first analyze the differences.

In this short blurb for my implementation (all that is left is to write the documentation), I focus on the compliance to the SRFI-37 wording and on differences in parsing to GNU C library's getopt_long.

Short refresher on SRFI-37

The procedure for parsing the command line is args-fold and it has the signature reproduced below.

procedure: (args-fold ARGS OPTIONS UNRECOGNIZED-OPTION-PROC OPERAND-PROC SEEDS ...)

The OPTIONS is a list with members created by the option procedure with the signature again reproduced below. In the examples in this post I will often pass #f as the OPTION-PROC, since it will be irrelevant.

procedure: (option NAMES REQUIRED-ARG? OPTIONAL-ARG? OPTION-PROC)

Short example putting this all together follows.

(use-modules (srfi srfi-37))

(define (option-processor opt name arg seed)
  (cons name seed))

(args-fold '("-a" "-b")
           (list (option '(#\a #\b) #f #f option-processor))
           #f #f '())
;;; => (#\b #\a)

Differences

Signaling an error on missing or extraneous arguments

Missing or extraneous argument for an option causes an error to be signaled.

(args-fold '("--a")
           (list (option '("a") #t #f #f))
           #f #f '())
;;; error--> In procedure args-fold: Missing required argument after `--a'

(args-fold '("--a=b")
           (list (option '("a") #f #f #f))
           #f #f '())
;;; error--> In procedure args-fold: Extraneous argument after `--a'

While this is often useful, I do not believe it actually complies to the specification. The specification is not very detailed, so we can only guess what the intended behavior is. Based on this part:

ARG will be a string, or #f if args-fold didn't encounter an option-argument.

Anthony Carrico, SRFI-37

I think that the correct behavior is to call the option processor and pass it #f as the argument value. The processor should inspect both the option and the argument, and signal an error if deemed appropriate.

I mean, both behaviors are fine. ¯\_(ツ)_/¯ But given this is supposed to be a fundamental, low-level option processing, it is better to err on the side of giving more power to the caller, not less.

An optional argument can be in a separate argument

When an option takes an optional argument, args-fold allows for it to be in a separate argument, while getopt_long does not. In other words, given (option '(#\a) #f #t #f) and command line -a b, args-fold parses it as an option "a" with argument value of "b". However getopt_long sees it as option "a" and operand "b".

Since optional arguments are somewhat rare, this is not a huge problem, but it prevents me from writing a program complying with section 12.1 of POSIX. And I like POSIX.

Long options do not accept value in a separate argument

This one is annoying and the main reason I even started looking into this. Passing an argument for long option in a separate argument, like --foo bar, does not work. It needs to be --foo=bar. This means that tilde (e.g. in --foo=~/Download) is not expanded.

No support for abbreviations

Did you know that you can abbreviate long options? No? Well me neither. But apparently, as long as the prefix is unique, you can shorten the options. So ls --versio is the same as ls --version.

Is this a good thing? Probably not. Should it be possible to turn this off? Yes. Is it possible to turn this off? No. Nevertheless, this is a parsing difference.

No support for POSIXLY_CORRECT

This is an environment variable that can be defined to make getopt_long stop parsing options on first operand. Sometimes it is useful, and I think it is a shame not to support it, especially since it is fairly easy.

Conclusion?

I will write my own args-fold. With blackjack. And hookers.