Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jun 22 22:54
    Negdayen closed #635
  • Jun 22 14:05
    Windows-mingw build of CI run 960844950 failed
  • Jun 22 13:40
    feeley commented #708
  • Jun 22 13:40

    feeley on master

    Fix gai_code_to_string type err… Add missing .gitignore paths Merge pull request #708 from Ne… (compare)

  • Jun 22 13:40
    feeley closed #708
  • Jun 20 03:14
    Negdayen synchronize #708
  • Jun 20 03:09
    Negdayen synchronize #708
  • Jun 20 03:07
    Negdayen synchronize #708
  • Jun 20 01:11
    Negdayen synchronize #708
  • Jun 19 20:13
    Negdayen opened #708
  • Jun 19 06:19
    lassik commented #707
  • Jun 19 06:13
    lassik opened #707
  • Jun 18 20:59
    gambiteer commented #706
  • Jun 18 14:27
    lassik commented #706
  • Jun 18 14:23
    Negdayen opened #706
  • Jun 18 10:51
    Windows-mingw build of CI run 949544601 failed
  • Jun 18 10:30

    feeley on master

    Fix documentation for index-ran… Merge pull request #702 from la… (compare)

  • Jun 18 10:30
    feeley closed #702
  • Jun 18 10:05
    lassik synchronize #705
  • Jun 18 09:45
    lassik opened #705
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
It requires a index.html that will load app.js.
youpi!
By the way, someone requested my guidance to help them start a frontend scheme project :)
There is more schemer, that we think!
Marc Feeley
@feeley
By “youpi!” I assume it works?