0

Suppose I have a collection of external data structures in a txt file. Whenever I need the data for computation I have to parse the file and then use it's values. How to make it more convenient?

  1. Keep the once parsed data as a complex list structure and write it in a SCM file with write. Then the full list can be simply read with read. Sure, but... (everytime the complex list representation of the data would have to be read and all the records would have to be formed afterwards - such could be avoided if the data had already been compiled)
  2. Can't I somehow keep the parsed result in a guile module? That is a compiled stuff once for all? I cannot find the right way to do it, though.

A simplified example:

Let the complex data be just a single number in an external file DATA.txt:

10

Let's create a module data.scm that reads in the external data, stores it and exports it:

(define-module (data))

(export DATA)

(define DATA (with-input-from-file "DATA.txt" read))

Let's test this approach with test.scm:

(add-to-load-path (dirname (current-filename)))

(use-modules (data))

(format #t "DATA: ~a\n" DATA)

THIS SEEMS TO WORK (auto-compilation is on or even guild compile data.scm is used):

$ guile test.scm
DATA: 10

However, when I delete the DATA.txt file, an error is raised since the (compiled) data module is missing the DATA.txt file for reading!

So the problem prevails: How to store the external data in the module at compile-time, actually?

WHAT WORKS - BUT IS UGLY!

To generate the module file literally, like:

(define DATA (with-input-from-file "DATA.txt" read))

(with-output-to-file "data.scm"
  (lambda ()
    (format #t
            "(define-module (data))

(export DATA)

(define DATA ~a)\n"
            DATA)))

This approach is like using the C macros and not taking advantage of the scheme's macros (i.e. syntax). Is there really no elegant scheme way?

4
  • The first time you import a module, Guile normally compiles it and caches the compiled version for reuse. Or you can compile it ahead of time with guild compile. So I'd look into your second option (by putting your data into its own module and importing it where needed) Commented Jun 19, 2024 at 18:47
  • Thank you, I see your point. My question was, perhaps, not clearly formulated. Now I provided also an example in a hope to show where the problem is. Commented Jun 19, 2024 at 22:50
  • Stick your data directly in the source instead of loading it. Or maybe something with a macro that reads it and generates a variable might work Commented Jun 20, 2024 at 2:08
  • Putting the data directly into the module is just what I wanted to avoid. Macro is a way (see my answer below), though still have some issues with syntax-case. Commented Jun 20, 2024 at 12:40

2 Answers 2

1

Here's an example of doing it using a syntax-case macro:

$ cat foo.scm
(define-module (foo)
  #:export (data))

(use-modules (ice-9 textual-ports))

(define-syntax include-file
  (lambda (stx)
    (syntax-case stx ()
      ((_ name)
       (let ((file-contents
              (call-with-input-file (syntax->datum #'name) get-string-all)))
         (datum->syntax #f file-contents))))))

(define data (include-file "./data.txt"))
$ cat data.txt
blah
$ sudo cp foo.scm /usr/share/guile/site/3.0/foo.scm
$ sudo guild compile -o /usr/lib64/guile/3.0/ccache/foo.go foo.scm
$ cd ..
$ guile
GNU Guile 3.0.9
Copyright (C) 1995-2023 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (use-modules (foo))
scheme@(guile-user)> data
$1 = "blah\n"

As you can see, after copying the source file to somewhere in guile's load path and compiling it to bytecode ahead of time, you can load the module from other locations (Or delete the data file) and the data is still available. But, really, it's easier to just have the data directly in a scheme file (Perhaps with the help of a program that creates such a file from the raw data that can be run when the data updates) and using guile's auto-compilation is a lot easier and less involved.

Sign up to request clarification or add additional context in comments.

1 Comment

Ooo, turns out guile has eval-when. That might lend itself to a different approach...
0

Using lispy macros, the data.scm module file could be coded like:

(define-module (data))

(export DATA)

(define-macro (define-get name file)
  `(define ,name
     ,(with-input-from-file file read)))

(define-get DATA "DATA.txt")

That's a sort of a solution. Still cannot write it correctly with syntax-case, which would make me happier.

And still don't get why the original (define DATA ...) did not work, as I always thought in scheme the args are first evaluated before used.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.