How to avoid loading cycle in Racket?
Asked Answered
O

1

6

I have quite simple set of .rkt sources and, say, "a.rkt" and "b.rkt" among them. I'd like to be able to write (require "a.rkt") in "b.rkt" and vice versa. Now I'm facing error about "loading cycle".

Can I solve this issue with bare modules without adding units? Does Racket have anything similar to forward declaration so I could simple add missing signature instead of requiring? If both answers are "No", does someone know good and understandable tutorial on how to implement units with typed/racket (aside of official docs)?

Orgeat answered 2/9, 2018 at 12:32 Comment(2)
A cyclical dependency is typically a code smell indicative of improper structuring of your program. Can you provide more details about your modules that might help us understand the necessity of your requirement?Irreverent
Sure, for example, I have some Engine and some Item, and Item has lambdas as fields and these lambdas take Engine object as first argument; later Engine is processing lists of Items. So Engine requires Item and Item requires EngineOrgeat
A
7

You can use lazy-require:

;; a.rkt
#lang racket
(require racket/lazy-require)
(lazy-require ["b.rkt" (b)])
(provide a)
(define (a) 'a)
(list (a) (b))

;; b.rkt
#lang racket
(require racket/lazy-require)
(lazy-require ["a.rkt" (a)])
(provide b)
(define (b) 'b)
(list (a) (b))

Notice that you must tell lazy-require the specific things you want to import. That's because it is implemented in terms of dynamic-require plus set!.

If you peek at the source for xrepl, you'll see it define a defautoload macro, which (modulo some N/A details) is simply:

(define-syntax-rule (defautoload libspec id ...)
  (begin
    (define id
      (make-keyword-procedure
       (λ (kws kw-args . args)
         (set! id (dynamic-require 'libspec 'id))
         (keyword-apply id kws kw-args args))))
    ...))
Artina answered 6/9, 2018 at 18:53 Comment(3)
Good, thank you! Do you know if there are any troubles using lazy-require with typed/racket?Orgeat
Oh. That wasn't clear to me from your question. Yes, there are troubles. If you're using Typed Racket I suggest using units (or make a fresh question here about writing your own typed lazy-require) -- if you're certain you really need mutually require-ing modules. But first: Check if you can use recursive definitions of Engine and/or Item with define-type? And/or use a "defs.rkt" or "types.rkt" that the other files require.Artina
Thank you very much, Greg. Now I understand that in different language I would try to use interfaces or traits. But this is quite different topic.Orgeat

© 2022 - 2024 — McMap. All rights reserved.