Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • May 05 15:01
    Travis gambit/gambit (v4.9.0) passed (1583)
  • May 05 14:51

    feeley on v4.9.0

    (compare)

  • May 05 14:51

    feeley on v4.9.0

    (compare)

  • May 05 13:52
    CI run 813517185 passed
  • May 05 13:16

    feeley on master

    Keep only the "INSIDE_EMACS" de… Merge branch 'master' of github… (compare)

  • May 04 17:59
    CI run 810839381 passed
  • May 04 17:19

    gambiteer on master

    Update SRFI 179 generalized-ar… (compare)

  • May 03 11:55
    CI run 806636559 passed
  • May 03 11:20

    feeley on master

    Remove six.prefix form (compare)

  • May 02 20:25
    CI run 804932802 passed
  • May 02 19:43

    feeley on master

    Universal backend: connect curr… (compare)

  • May 02 17:33
    MacOS build of CI run 804702347 failed
  • May 02 17:16

    feeley on master

    Universal backend: allow creati… (compare)

  • May 02 15:03
    lassik opened #688
  • May 02 04:12
    Windows-mingw build of CI run 803525339 failed
  • May 02 03:46

    feeley on master

    Universal backend: add meta-inf… (compare)

  • May 01 16:25
    CI run 802492973 passed
  • May 01 15:46

    feeley on master

    Test simpler CI Windows/mingw b… (compare)

  • May 01 15:26
    Windows-mingw build of CI run 802422058 failed
  • May 01 15:01

    feeley on master

    Fix issue on Windows/mingw with… (compare)

amirouche
@amirouche
Speaking of library system and JS target, it would be easier for me if it produced a single .js file.
Marc Feeley
@feeley
@amirouche nice to hear your praise of Gambit (current and future)!
what do you mean “if it produced a single .js file”? that is what is produced by -exe… and if you want that wrapped in a trivial HTML you can -exe -o foo.html
amirouche
@amirouche
Last time I tried to compile a program.scm with (import (foobar)), there was several messages in the browser console about failed attempts to retrieve foobar.sld over the network.
I use -:r7rs
Marc Feeley
@feeley
if you do a static link then the dynamic module loading will not happen (unless you forgot to link the module in!)
amirouche
@amirouche
I will look into static link.
Marc Feeley
@feeley
one issue with the current static linker is that you have to help it out with locating the modules (ideally it should be automatic and this is on my TODO)… also you have to use the -nopreload linker option
amirouche
@amirouche
Re JS promise, does the resolution of promise block or pause only the current thread? Hence it would be possible to spawn two threads and do a fetch in each of them?
Related question is: is call/cc fast? In my prototype, I use call/cc to implement "non-colored async that look sync" similar to what Gambit has builtin with the lightweight threads and network io.
Ultimately, what would be the best way to implement network io browser side without blocking.
Marc Feeley
@feeley
yes waiting for promise results is done on a thread by thread basis (that’s one of the difficulties in implementing this and frankly it would lose a lot of value if it wasn’t that way)
yes call/cc is “fast” (the Scheme thread scheduler uses call/cc or rather continuation-capture to switch threads)
what is “network i/o”? fetch?
you can spawn N Scheme threads, each doing a JS fetch and it will all be concurrent (it is one of the examples in the paper I shared with you, which will be presented at ELS21 by the way)
amirouche
@amirouche
By "network i/o" I mean fetch and websockets.
Marc Feeley
@feeley
all of that currently works well

try this on https://gambitscheme.org/try

(define-syntax future
  (lambda (stx)
    (syntax-case stx ()
      ((future expr)
       #'(thread (lambda () expr))))))

(define touch thread-join!)

(define (pmap f lst)   ;; "parallel" map
  (map touch (map (lambda (x) (future (f x))) lst)))

(define memo
  (string-append
   "Scheme_-_An_interpreter_for_extended_"
   "lambda_calculus.djvu"))

(define (page n)
  (string-append
   "https://upload.wikimedia.org/wikipedia"
   "/commons/thumb/1/1e/" memo
   "/page" (number->string n) "-593px-" memo ".jpg"))

(define (fetch-blob url)
  \fetch(`url).then(function (r) { return r.blob(); }))

(define (->URL blob)
  \URL.createObjectURL(`blob))

(define (show url)
  \document.body.insertAdjacentHTML(
   "beforeend",
   "<img src='"+(`url)+"' width=200px>"))

(define images
  (pmap (lambda (n) (->URL (fetch-blob (page n))))
        (iota 43 1)))

(for-each show images)

you should see all the pages appear quickly… if you replace pmap by map it will be 10x slower

amirouche
@amirouche
Indeed! Thanks again for your precious time.
Drew Crampsie
@drewc
OMG!! I have not tried that part yet but am about to ... we auto-resolve promises? So the async appears synchronous in (use-result-value (promise-value))??
drewc @drewc REPL'ies to himself ...
Drew Crampsie
@drewc
Oh that is very very cool.
amirouche
@amirouche
It is not useful for my immediate need of Gambit, but it will inspire more confidence going forward: Is there a readily available documentation regarding the Universal backend? Otherwise, what is the core code or entry point into the Universal backend?
Regarding the infix notion called SIX, it is already usable for interfacing with C? Otherwise, is it planned or an interesting feature ?
amirouche
@amirouche
Instead of "inspire confidence" it should be written "increase confidence".
Marc Feeley
@feeley

@amirouche it depends what kind of documentation you are looking for… there are academic papers about how the universal backend works (see for example https://www.researchgate.net/publication/304551221_Compiling_for_multi-language_task_migration). There is no “software developper” documentation however. Here’s a quick overview. The files implementing the universal backend are gsc/_t-univ-*.scm . In essence they implement a translator from the GVM (Gambit Virtual Machine) code and the target language (JavaScript, Python, etc). There are 2 main entry point to the universal backend: the procedure univ-dump-code (which implements GVM -> target) and the procedure univ-link (which implements the static module linker). Both of these procedures, and other methods of the backend are contained in a target object that is initialized by the univ-setup procedure:

(univ-setup 'js     '((".js"   . JavaScript))  '()        '())
(univ-setup 'python '((".py"   . Python))      '((pre3))  '())
(univ-setup 'ruby   '((".rb"   . Ruby))        '()        '())
(univ-setup 'php    '((".php"  . PHP))         '((pre53)) '())

These calls define the targets that can be used on the gsc command line, i.e. -target js, -target python, etc The fields of the target object are documented in gsc/_back.scm. For example:

;; nb-regs      Integer denoting the maximum number of GVM registers
;;              that should be used when generating GVM code for this
;;              target machine.
;;
;; nb-arg-regs  Integer denoting the maximum number of procedure call
;;              arguments that are passed in GVM registers.
;;
;; compactness  Integer denoting the level of compactness of the
;;              generated code.  Levels from 0 to 5 cause the
;;              generation of increasingly compact code with little or
;;              no impact on execution speed.  Lower values tend to
;;              make the generated code more humanly readable.  Above
;;              a level of 5 the compiler will trade execution speed
;;              for saving code space.  The detailed meaning of this
;;              option depends on the target and some targets may
;;              ignore it.

The universal backend implements the code generation for multiple target languages by using code generation macros. These have names starting with a ^, for example (^if test true [false]) generates a 1 or 2 branch “if” construct in the target language and (^ X Y Z) concatenates the target code X, Y and Z. The ^if macro is implemented through the univ-emit-if procedure which dispatches on the target language:

(define (univ-emit-if ctx test true #!optional (false #f))
  (case (target-name (ctx-target ctx))

    ((js php java)
     (^ "if (" test ") {\n"
        (^indent true)
        (if false
            (^ "} else {\n"
               (^indent false))
            (^))
        "}\n"))

    ((python)
     (^ "if " test ":\n"
        (^indent true)
        (if false
            (^ "else:\n"
                  (^indent false))
            (^))))

    ((ruby)
     (^ "if " test "\n"
        (^indent true)
        (if false
            (^ "else\n"
               (^indent false))
            (^))
        "end\n"))

    ((go)
     (^ "if " test " {\n"
        (^indent true)
        (if false
            (^ "} else {\n"
               (^indent false))
            (^))
        "}\n"))

    (else
     (compiler-internal-error
      "univ-emit-if, unknown target"))))

The implementation of Scheme primitives is done through the univ-define-prim macro. For example here is the implementation of ##fxmin that returns the minimum of two fixnums:

(univ-define-prim "##fxmin" #t
  (make-translated-operand-generator
   (lambda (ctx return arg1 arg2)
     (return (^if-expr 'scmobj
                       (^< (^fixnum-unbox arg1) (^fixnum-unbox arg2))
                       arg1
                       arg2)))))
Marc Feeley
@feeley
The ^if-expr macro generates conditional expressions. Note that the first parameter is a type (the type of the value returned by the conditional expression) because some targets need to have this information, namely the go target implements conditional expressions using a function call:
(define (univ-emit-if-expr ctx type expr1 expr2 expr3)
  (case (target-name (ctx-target ctx))

    ((js ruby java)
     (^ expr1 " ? " expr2 " : " expr3))

    ((php)
     (^parens (^ expr1 " ? " expr2 " : " expr3)))

    ((python)
     (^ expr2 " if " expr1 " else " expr3))

    ((go)
     (^apply (univ-emit-fn-decl
              ctx
              #f
              type
              '()
              (^if expr1
                   (^return expr2)
                   (^return expr3)))
             '()))

    (else
     (compiler-internal-error
      "univ-emit-if-expr, unknown target"))))
amirouche
@amirouche
I noted those information and others in the following document: https://github.com/amirouche/gambit-scheme-pawns#readme
I would be happy if someone could review at least the last three questions: https://github.com/amirouche/gambit-scheme-pawns#how-does-termite-compare-to-concurrentml
Marc Feeley
@feeley
The ELS2021 paper on the JavaScript foreign function interface is now online (https://zenodo.org/record/4711425). It will be presented at ELS on May 4 (see the schedule here: https://european-lisp-symposium.org/2021/index.html)
amirouche
@amirouche
It was added as reference in gambit pawns (previous link)
How does the namespace ## and the empty namespace relate to (import (gambit)) in a R7RS library ?
amirouche
@amirouche
I got it, the (gambit) library is defined in gambit.sld which expose procedures from the empty namespace.
jgart
@jgarte:matrix.org
[m]
Come to today's nixnet featured event at 2PM EST and join us for some guix packaging: https://events.nixnet.services/
philsuero
@philsuero
@feeley You have an email, please respond, also this (based on ___STILL) works both with the default GC and also with a later brooks GC right?
Marc Feeley
@feeley
I have answered… Which brooks GC are you talking about?
amirouche
@amirouche
How can I compile a R7RS scheme project that rely on libraries into a single .js file, I looked into -link but I got lost. It generate several .js and _.js file, but then can not manage to assemble them into an executable with -exe flag.
Marc Feeley
@feeley

@amirouche If an R7RS app has 2 modules mod1.sld and mod2.sld then here is how to compile and link them:

% cat mod1.sld
;; File: mod1.sld
(define-library (mod1)
  (export hi)
  (import (scheme base) (scheme write))
  (begin
    (define (hi) (display "hello\n"))))
% cat mod2.sld
;; File: mod2.sld
(define-library (mod2)
  (import (scheme base) (mod1))
  (begin
    (define (main) (hi) (hi))
    (main)))
% gsc -target js -link . -nopreload mod1.sld mod2.sld
mod1.sld:
mod2.sld:
% cat mod2_.js mod1.js mod2.js `gsc -i -e '(display (path-expand "~~lib/_gambit.js"))'` > app.js
% node app.js
hello
hello
% cat mod2_.js `gsc -i -e '(display (path-expand "~~lib/_gambit.js"))'` mod2.js mod1.js > app.js
% node app.js
hello
hello
% cat mod1.js mod2.js `gsc -i -e '(display (path-expand "~~lib/_gambit.js"))'` mod2_.js > app.js
% node app.js
/Users/feeley/app.js:4
_cst0__mod1 = new _ScmString([104,101,108,108,111,10]);
^

ReferenceError: _ScmString is not defined
    at Object.<anonymous> (/Users/feeley/app.js:4:1)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

Note that the order in which the generated .js files are concatenated has no importance, except that the “link file” (here mod2_.js) must come first because it contains the runtime system functions needed to create Scheme objects and “register” each module in the other files. When all modules are “registered” the Scheme code will start executing. In principle (untested) after the link file is loaded, the other files could even be loaded asynchronously. If you prefer to manually start the app (say after other JS modules and a UI component are initialized) then you should do:

@all_modules_registered@ = function () { };

and at the point where you want to start the execution of the Scheme code:

@program_start@();
Marc Feeley
@feeley

Note that the @s are special notation to abstract away from the actual name used by the Gambit compiler, which could be a short name if you specify a compactness option above 0. To use the @ feature you need to write:

(##inline-host-declaration “
@all_modules_registered@ = function () { };
function my_program_start() { @program_start@(); }
”)

and call my_program_start() in your web app.

amirouche
@amirouche
Thanks!
amirouche
@amirouche
I guess, if there is no command such as gsc -target javascript pack program.scm that produce a single .js file, it is because it is not easy, and there is nobody to do the required work?
Marc Feeley
@feeley
what would that option mean? as I said -target js -exe -o foo.js generates a single .js file
amirouche
@amirouche
yes, but it try to fetch libraries over the network.
Marc Feeley
@feeley
unless you statically link them… gsc -target js -exe a.scm b.scm c.scm
amirouche
@amirouche
oh, I will try that.
Marc Feeley
@feeley
you also need the -nopreload option if the modules are R7RS libraries
the current issue is that finding all the required libraries for the static link is not done automatically (but dynamically loading the libraries works if that is an option for your app)
amirouche
@amirouche
it works.
indeed.

Here is my test program:

❯ cat webui.scm
;; required to use javascript notation inside Scheme.
(import (_six js))
(import (scheme base))

(define (console-log obj)
  \console.log(`obj))

(define (document-query-selector selector)
  \document.querySelector(`selector))

(console-log 42)

(console-log (document-query-selector "body"))

Here is the cli dance:

gsc -target js -exe -nopreload $(pwd)/../../../gambit/lib/scheme/base/base.sld $(pwd)/../../../gambit/lib/_six/js.sld webui.scm && mv webui app.js && python3 -m http.server