Jean-Baptiste
Jean-Baptiste

Reputation: 31

Racket: enable scribble language in sub module

#lang racket/base
(module x scribble/text
 @(display 123))

It seems like #lang statements are not valid in nested sub-modules, and the expanded module version above is missing something:

error: module: no #%module-begin binding in the module's language

update:

looks like this more or less works, but is there a better way? does scribble do something with output ports that isn't being handled?

#lang racket/base
(module x scribble/text/lang
  (#%module-begin
   #reader scribble/reader @list{
     hi
     @(+ 1 456)
  }))

Upvotes: 2

Views: 261

Answers (1)

Eli Barzilay
Eli Barzilay

Reputation: 29546

First, your code has a redundant #%module-begin which can be removed.

#lang does several things -- one is control the semantics of the file by determining the set of initially imported bindings, and that's something that the module form has done before #lang came up. With submodules, it became possible to use module for parts of a file too. However, #lang can also determine the reader that parses the file, and that's not possible to do with submodules, so you're stuck with only one toplevel #lang to set the parser for the whole file.

(Sidenote: There is a technical reason for that. A #lang reader reads the rest of the file until it reaches an eof value, so a nested #lang would require getting an eof value before getting the end of the file, or adding a new kind of eof-like value. That means that it's a change that should be done carefully -- it's possible to do, of course, but the need didn't come up often enough. Hopefully it will, in the future.)

But in your case you don't want a completely new concrete syntax, just an extension for s-expressions -- and an extension that was chosen to have a minimal impact on regular code. So in almost all cases it's fine to just enable the @-form syntax for the whole file, and then use @-forms where you want it. Since it's just an alternative way for reading sexprs, you can even use that with module, leading to this code that doesn't need to use #reader:

#lang at-exp racket/base
@module[x scribble/text/lang]{
  hi
  @(+ 1 456)
}
(require 'x)

One thing that is a bit strange here is using scribble/text/lang and not just scribble/text. Usually, #lang foo is exactly the same as (module x foo ...) after reading the code with foos reader. But in the case of the scribble/text language there is another difference: using it as a #lang makes the semantics of the module body be "output each thing". The idea is that as a language you'll want to spit out mostly-text files, but as a library you'll want to write code in it and do the printout yourself.

Since this code uses module, using scribble/text means that you're not getting the spit-all-out functionality, which is why you need to explicitly switch to scribble/text/lang. But you could have instead just do the spitting yourself using the language's output, which would give you this code:

#lang at-exp racket/base
(module x racket/base
  (require scribble/text)
  (output @list{
    hi
    @(+ 1 456)}))
(require 'x)

Note that scribble/text is not used as a language here, since it doesn't provide enough stuff to be one when used (outside of a #lang). (Which you've found out, leading to that redundant #%module-begin...)

This version is slightly more verbose, but I'm guessing that it makes more sense in your case, since using it for some part of the code means that you want to use it as a library.

Finally, if you really don't want to read the whole file with the @ syntax, only some parts, then the #reader that you've found is perfectly fine. (And this is made simple with scribble/text that treats lists as concatenated outputs, so you need just one wrapper for each chunk of text.)

Upvotes: 4

Related Questions