Proper usage of make-base-eval-factory

Say I have a module my-pkg/foo.rkt which exports bar.
The doc mypkg/scribblings/foo.scrbl uses scribble/example like:

@(define (make-my-eval) (make-base-eval '(require "../foo.rkt")))

@examples[#:eval (make-my-eval) bar]

This works fine in DrRacket, but fails with raco setup. To make it work for the latter it seems I need instead:

@(define (make-my-eval) (make-base-eval '(require "foo.rkt")))

although this isn't consistent path-wise.
Ideally I would use

@(define make-my-eval (make-base-eval-factory (list "../foo.rkt")))

but I couldn't make this work, either with or without "../", or runtime paths (converted to strings, which also seems wrong).

I found only very few instances of make-base-eval-factory on github or Google. I've tried quite a number of combinations, but at this stage I'm really confused and can't form a correct model of how it works (in particular what's the current-directory when). So what's the proper way to use make-base-eval-factory in the setup above?

Also

@(define (make-my-eval) (make-base-eval '(require my-pkg/foo)))

works in both DrRacket and with raco setup, but I'm not convinced that's the right way to write it, as my-pkg/foo might refer to a differently installed package than the one for which the doc is being generated — or actually not?

And anyway, this still doesn't work:

@(define make-my-eval (make-base-eval-factory '(my-pkg/foo)))

Perhaps examples from GitHub may help?

Your link doesn't work it seems.

I already looked at github and didn't find anything relevant enough — though possibly I missed some.

FWIW here is a snippet from the documentation for Sketching.

@; Note: The documentation won't build on the package server if racket/gui is instantiated.
@;       This means we need to use racket/draw and sketching/exports-no-gui
@;       instead of racket/gui and sketching.
@(define draw-namespace (make-base-namespace))
@(define factory        (make-base-eval-factory (list 'racket/draw)))
@(define (make-sketching-eval)   
   (let ([e (factory)])
     (e '(require sketching/exports-no-gui sketching/parameters racket/draw))
     (e '(define (new-bitmap-dc w h)
           (define dc (new bitmap-dc% [bitmap (make-bitmap w h)]))
           (send dc set-smoothing 'aligned)
           dc))
     (e '(current-dc (new-bitmap-dc 100 100)))
     e))
@(define se (make-sketching-eval))

I see that I avoided relative filenames and instead went with sketching/exports-no-gui and sketching/parameters. The alternative must be to use runtime paths.

1 Like

You also don't include the ad-hoc requires in the factory list, which suggests this list is only for 'core' libraries? I also couldn't use it for my ad-hoc libraries (which kind of defeats the purpose of using make-eval-base-factory I suppose).

I would have thought something like:

(make-base-eval-factory (list 'my-pkg/foo)

would have worked.

That path is independent of the current directory.

1 Like

The documentation says that the rel-string variant of root-module-path is “relative to the containing source (as determined by current-load-relative-directory or current-directory)”. The sandbox functions do top-level evaluation, so those parameters are consulted at runtime, i.e. not when compiling your module that uses make-base-eval-factory.

DrRacket parameterizes current-directory to the directory containing the file for which you pressed the “Run” button. I don't think raco setup specifies how those parameters are set: it might be the OS-level current directory from which you invoke raco setup, or it might be some other directory convenient for raco setup's purposes. Running a program with racket at the command line definitely does use the OS-level working directory in which the process is started.

I'm not sure what you mean by “might refer to a differently installed package than the one for which the doc is being generated”.

I would definitely refer to a module that is part of a collection as my-pkg/foo or (lib "my-pkg/foo.rkt").

Nonetheless, it is possible to refer to a non-collection-based module using make-base-eval-factory. For example, considering the following directory layout:

philip@bastet:~$ tree /tmp/mpi
/tmp/mpi
|-- count.rkt
`-- eval.rkt

1 directory, 2 files

where count.rkt is:

#lang racket/base
(provide count!)
(define n 0)
(define (count!)
  (begin0 n
    (set! n (add1 n))))

and eval.rkt is:

#lang racket
(require scribble/example
         racket/runtime-path)
(define-runtime-path count.rkt "count.rkt") ; [1]
count.rkt
(define make-count-eval
  (make-base-eval-factory (list count.rkt)))
(current-load-relative-directory)
(current-directory)
(define e
  (make-count-eval))
(e `(#%require ,count.rkt)) ; [2]
(e `(list (count!) (count!) (count!)))
((make-count-eval) `(begin (#%require ,count.rkt) ; [3]
                           (count!))) ; [4]

Here's how it works:

philip@bastet:~$ racket /tmp/mpi/eval.rkt
#<path:/tmp/mpi/count.rkt>
#f
#<path:/home/philip/>
'(0 1 2)
0
philip@bastet:~$ cd /tmp/mpi/
philip@bastet:/tmp/mpi$ racket eval.rkt
#<path:/tmp/mpi/count.rkt>
#f
#<path:/tmp/mpi/>
'(0 1 2)
0

Some notes:

  1. I used define-runtime-path because the resulting complete path? satisfies module-path?. Usually you'd want to use define-runtime-module-path-index or define-runtime-module-path, but make-base-eval-factory wants a (listof module-path?), and I couldn't figure out how to turn a module-path-index? or a resolved-module-path? into a module-path?. It seems possible that make-base-eval-factory might be able to accept something like (listof (or/c module-path? resolved-module-path?)), or maybe there's a conversion I missed.
  2. A complete path? is accepted by #%require, but not require.
  3. You need to #%require the module again for the new evaluator. Using make-eval-factory, instead, would require the modules automatically.
  4. I was surprised that this returned 0, not 3, given that the docs for make-base-eval-factory say that “the modules are loaded and instantiated once (when the returned make-base-eval-like function is called the first time) and then attached to each evaluator that is created”.
1 Like

@soegaard This works (both in drr and raco setup):

@(define make-my-eval
   (λ () (make-base-eval '(require my-pkg/foo))))

but this doesn't (in either drr or raco setup):

@(define make-my-eval
   (make-base-eval-factory '(my-pkg/foo)))

@LiberalArtist

I'm not sure what you mean by “might refer to a differently installed package than the one for which the doc is being generated”.

I was using (require my-pkg/foo) in my test files, and I was wondering why all my tests were passing despite an obviously-wrong code. It took me a while to realize that I had 2 versions of the package, and the one I was editing was not the one that referred to my-pkg as a collection. After a raco pkg update --link ... it all worked. I was thinking that some similar mistake could happen for package documentation, but actually maybe not.

@LiberalArtist Thanks for the detailed answer! It seems like using make-base-eval-factory is more complicated than I thought at first, so I'll just stick to repeated make-base-eval then.

Thanks; link fixed (the mail parser mangles Markdown-syntax links sometimes, I think)

1 Like