Changeset 34230 in project

07/12/17 15:42:37 (2 weeks ago)

Anonymous wiki edit for IP []: update nrepl documentation from latest

1 edited


  • wiki/eggref/4/nrepl

    r30904 r34230  
    11== NREPL
    3 An blocking networked REPL for Chicken Scheme. Each new incoming
    4 connection runs in a new srfi-18 thread.
     3A networked REPL for Chicken Scheme. Each new incoming connection runs
     4in a new {{srfi-18}} thread.
    77=== Requirements
    8 None except the core tcp and srfi-18 units.
     8None except the {{tcp}} and {{srfi-18}} units from CHICKEN core.
    1111=== API
    12 <procedure> (nrepl <port> [spawn])</procedure>
     12<procedure> (nrepl port [spawn])</procedure>
    14 Listen to TCP port <port> and wait for incoming connections, doing
    15 {{(spawn thunk)}} for each. {{spawn}} defaults to {{thread-start!}}.
     14Listen to TCP port {{port}} and (blockingly) wait for incoming
     15connections.  {{(spawn in out)}} is called for each incomming
     16connection, where {{in}} is the input port and {{out}} is the output port
     17for the new TCP session.
     19You can use {{spawn}}, for example, for authentication:
     21<enscript highlight="scheme">   
     22(nrepl 1234
     23       (lambda (in out)
     24         (thread-start! ;; otherwise accept-loop will be blocked
     25          (lambda ()
     26            (display ";; please enter an accept token: " out)
     27            (define token (read-line in))
     28            (if (equal? token "abc")
     29                (nrepl-loop in out)
     30                (begin (display ";; access denied\n" out)
     31                       (close-input-port in)
     32                       (close-output-port out)))))))
     35blockquoteYou can use {{(tcp-addresses in)}} and {{(tcp-port-numbers in)}} to find
     36out where the new session is coming from.
     38{{nrepl}} will loop for accepting incomming connections unless {{spawn}}
     39returns {{#f}}.
    18 === Example
    19 At the very beginning of your application, you can get network REPL
    20 access by doing this:
     42=== Practical use
     43Any source-code you send down a {{nrepl}} session will not be persisted
     44anywhere.  You can reset your program state by restarting your program
     45which may be useful sometimes.
     48==== Terminal users
     49Editing code directly from {{nc localhost 1234}} isn't
     50pleasant. Luckily, [[|rlwrap]] works along {{nrepl}} to improve this
     53<enscript highlight="bash">   
     54 âž€ rlwrap nc localhost 1234
     55;; nrepl on (csi -s example.scm)
     56#;1> (define (hello) (print "this will be in my history"))
     59[[|rlwrap]] will also save your read-line history for the next invokation
     60{{rlwrap nc localhost 1234}} which is handy!
     63==== [[|Emacs]] users
     64{{nrepl}} plays very nicely with [[|Emacs]]! If you're used to running {{M-x
     65run-scheme}} and sending source-code from buffers into your REPL, an
     66{{nrepl}} endpoint can be used as a Scheme "interpreter". Specify that
     67you want to run {{nc localhost 1234}} for your Scheme interpreter
     68instead of the usual {{csi}}, and you get the functionality you're used
     71Note that telling [[|Emacs]] that {{nc localhost 1234}} is your Scheme
     72interpreter is tricky because {{C-u M-x run-scheme}} will not let you
     73enter spaces. This can be solved by pressing {{C-q}} before pressing
     77==== Example HTTP-server work-flow
     78A real-world use-case for {{nrepl}} might be something like the
     79following. Let's make a simple hello-world HTTP server using [[|spiffy]].
    2281<enscript highlight="scheme">   
     82(use spiffy nrepl)
     84(define (app c)
     85  (send-response body: "hello world\n"))
     88 (lambda ()
     89   (vhost-map `((".*" . ,(lambda (c) (app c)))))
     90   (start-server)))
     92(print "starting nrepl on port 1234")
     93(nrepl 1234)
     96Now spiffy runs on port {{8080}}:
     98<enscript highlight="bash">   
     99 âž€ curl localhost:8080
     100hello world
     103What's nice about this is that, since {{app}} is a top-level variable,
     104it can be replaced from the REPL:
     106<enscript highlight="bash">   
     107 âž€ nc localhost 1234
     108;; nrepl on (csi -s example.scm)
     109#;1> (define (app c) (send-response body: "repl hijack!\n"))
     110#;1> ^C
     113Now {{spiffy}} will use our top-level {{app}} for its proceeding requests:
     115<enscript highlight="bash">   
     116 âž€ curl localhost:8080
     117repl hijack!
     120Note that {{app}} must be wrapped in a {{lambda}} for this to work,
     121because the REPL can only replace top-level variable definitions.
     123The implications of this can be quite dramatic in terms of
     124work-flow. If you write your app in a REPL-friendly way like this, you
     125can modify you program behaviour on-the-fly from the REPL and never
     126have to restart your process and lose its state.
     129==== Example CPU-intensive main thread
     130{{nrepl}} can be used for live-coding interactive application such as
     131games. Adding {{(thread-start! (lambda () (nrepl 1234)))}} usually Just
     132Works, where you can redefine top-level function and game state
     135However, if the game-loop is eating up a lot of scheduler-time, you
     136may find that your REPL becomes unresponsive. A good way to fix this
     137is to wrap both the REPL and the game-loop in a mutex. This has
     138another advantage in that it will ensure your REPL will not interfere
     139with game-state (or OpenGL state) during game-loop iteration.
     141<enscript highlight="scheme">   
     142;;; wrapping nrepl eval in a mutex for responsiveness
     143;;; and game-loop thread-safety. running this and then doing:
     144;;;     echo '(thread-sleep! 1)' | rlwrap nc localhost 1234
     145;;; should pause the game-loop for 1 second
    23146(use nrepl)
    24 (define repl-thread (thread-start! (lambda () (nrepl 1234))))
     148(define with-main-mutex
     149  (let ((main-mutex (make-mutex)))
     150    (lambda (proc)
     151      (dynamic-wind (lambda () (mutex-lock! main-mutex))
     152                    proc
     153                    (lambda () (mutex-unlock! main-mutex))))))
     156 (lambda ()
     157   (nrepl 1234
     158          (lambda (i o)
     159            (thread-start!
     160             (lambda ()
     161               (nrepl-loop
     162                i o (lambda (x) (with-main-mutex (lambda () (eval x)))))))))))
     164(define (game-step)
     165  (print* "\r"  (current-milliseconds) "   ")
     166  (thread-sleep! 0.05))
     168(let loop ()
     169  (with-main-mutex game-step)
     170  (loop))
     174==== {{nrepl}} in compiled code
     175{{nrepl}} also works inside a compiled program. However, sometimes
     176[[|modules]] disappear due to compiler optimizations.
     179==== {{nrepl}} on Android
     180{{nrepl}} has been used successfully on Android target hardware for
     181remote interactive development.  Check
     182out [[|this]]
     183Android example project.
    28186=== Source code repository
    29 You can find the source [[|here]].
     187You can find the
     188source [[|here]].
    36195=== License
Note: See TracChangeset for help on using the changeset viewer.