source: project/wiki/eggref/4/nrepl @ 35452

Last change on this file since 35452 was 35452, checked in by svnwiki, 18 months ago

Anonymous wiki edit for IP [213.52.73.118]: nrepl doc from its readme (markdown-svnwiki)

File size: 6.4 KB
Line 
1== NREPL
2[[toc:]]
3A networked REPL for Chicken Scheme. Each new incoming connection runs
4in a new {{srfi-18}} thread.
5
6
7=== Requirements
8None except the {{tcp}} and {{srfi-18}} units from CHICKEN core.
9
10
11=== API
12<procedure> (nrepl port [spawn])</procedure>
13
14Listen to TCP port {{port}} number and (blockingly) wait for incoming
15connections.  {{(spawn)}} is called for each incomming connection
16without arguments where {{current-input-port}}, {{current-output-port}}
17and {{current-error-port}} are bound to the TCP connection.
18
19You can use {{spawn}}, for example, for authentication:
20
21<enscript highlight="scheme">    
22(nrepl 1234
23       (lambda ()
24         (thread-start! ;; otherwise accept-loop will be blocked
25          (lambda ()
26            (display ";; please enter an accept token: ")
27            (define token (read-line))
28            (if (equal? token "abc")
29                (nrepl-loop)
30                (begin (print ";; access denied")
31                       (close-input-port (current-input-port))
32                       (close-output-port (current-output-port))
33               (close-output-port (current-error-port))))))))
34</enscript>
35
36blockquoteYou can use {{tcp-addresses}} and {{tcp-port-numbers}} to find out where
37the new session is coming from.
38
39{{nrepl}} will loop for accepting incomming connections unless {{spawn}}
40returns {{#f}}.
41
42<procedure> (nrepl-loop #!key eval read print writeln)</procedure>
43
44Start a standard REPL-loop: print the prompt, read an s-expression,
45evaluate the expression, print the result and repeat. Exceptions are
46reported and data is flushed. This can be used inside the optionally
47supplied {{spawn}}-procedure above.
48
49
50=== Practical use
51Any source-code you send down a {{nrepl}} session will not be persisted
52anywhere.  You can reset your program state by restarting your program
53which may be useful sometimes.
54
55
56==== Terminal users
57Editing code directly from {{nc localhost 1234}} isn't
58pleasant. Luckily, [[http://freecode.com/projects/rlwrap|rlwrap]] works along {{nrepl}} to improve this
59experience:
60
61<enscript highlight="bash">    
62 âž€ rlwrap nc localhost 1234
63;; nrepl on (csi -s example.scm)
64#;1> (define (hello) (print "this will be in my history"))
65</enscript>
66
67[[http://freecode.com/projects/rlwrap|rlwrap]] will also save your read-line history for the next invokation
68{{rlwrap nc localhost 1234}} which is handy!
69
70
71==== [[https://www.gnu.org/software/emacs/|Emacs]] users
72{{nrepl}} plays very nicely with [[https://www.gnu.org/software/emacs/|Emacs]]! If you're used to running {{M-x
73run-scheme}} and sending source-code from buffers into your REPL, an
74{{nrepl}} endpoint can be used as a Scheme interpreter. You can specify
75that you want to use {{nrepl}} with a prefix. For example:
76
77    C-u M-x run-scheme RET nc localhost 1234
78
79Note that telling [[https://www.gnu.org/software/emacs/|Emacs]] that {{nc localhost 1234}} is your Scheme
80interpreter is tricky because {{C-u M-x run-scheme}} might not let you
81enter spaces. You can enter spaces by pressing {{C-q}} before pressing
82space.
83
84
85==== Example HTTP-server work-flow
86A real-world use-case for {{nrepl}} might be something like the
87following. Let's make a simple hello-world HTTP server using [[http://api.call-cc.org/doc/spiffy|spiffy]].
88
89<enscript highlight="scheme">    
90(use spiffy nrepl)
91
92(define (app c)
93  (send-response body: "hello world\n"))
94
95(thread-start!
96 (lambda ()
97   (vhost-map `((".*" . ,(lambda (c) (app c)))))
98   (start-server)))
99
100(print "starting nrepl on port 1234")
101(nrepl 1234)
102</enscript>
103
104Now spiffy runs on port {{8080}}:
105
106<enscript highlight="bash">    
107 âž€ curl localhost:8080
108hello world
109</enscript>
110
111What's nice about this is that, since {{app}} is a top-level variable,
112it can be replaced from the REPL:
113
114<enscript highlight="bash">    
115 âž€ nc localhost 1234
116;; nrepl on (csi -s example.scm)
117#;1> (define (app c) (send-response body: "repl hijack!\n"))
118#;1> ^C
119</enscript>
120
121Now {{spiffy}} will use our top-level {{app}} for its proceeding requests:
122
123<enscript highlight="bash">    
124 âž€ curl localhost:8080
125repl hijack!
126</enscript>
127
128Note that {{app}} must be wrapped in a {{lambda}} for this to work,
129because the REPL can only replace top-level variable definitions.
130
131The implications of this can be quite dramatic in terms of
132work-flow. If you write your app in a REPL-friendly way like this, you
133can modify you program behaviour on-the-fly from the REPL and never
134have to restart your process and lose its state.
135
136
137==== Example CPU-intensive main thread
138{{nrepl}} can be used for live-coding interactive application such as
139games. Adding {{(thread-start! (lambda () (nrepl 1234)))}} usually Just
140Works, where you can redefine top-level function and game state
141on-the-fly.
142
143However, if the game-loop is eating up a lot of scheduler-time, you
144may find that your REPL becomes unresponsive. A good way to fix this
145is to wrap both the REPL and the game-loop in a mutex. This has
146another advantage in that it will ensure your REPL will not interfere
147with game-state (or OpenGL state) during game-loop iteration.
148
149<enscript highlight="scheme">    
150;;; wrapping nrepl eval in a mutex for responsiveness
151;;; and game-loop thread-safety. running this and then doing:
152;;;     echo '(thread-sleep! 1)' | rlwrap nc localhost 1234
153;;; should pause the game-loop for 1 second
154(use nrepl)
155
156(define with-main-mutex
157  (let ((main-mutex (make-mutex)))
158    (lambda (proc)
159      (dynamic-wind (lambda () (mutex-lock! main-mutex))
160                    proc
161                    (lambda () (mutex-unlock! main-mutex))))))
162
163(thread-start!
164 (lambda ()
165   (nrepl 1234
166          (lambda ()
167            (thread-start!
168             (lambda ()
169               (nrepl-loop eval: (lambda (x) (with-main-mutex (lambda () (eval x)))))))))))
170
171(define (game-step)
172  (print* "\r"  (current-milliseconds) "   ")
173  (thread-sleep! 0.05))
174
175(let loop ()
176  (with-main-mutex game-step)
177  (loop))
178</enscript>
179
180
181==== {{nrepl}} in compiled code
182{{nrepl}} also works inside a compiled program. However, sometimes
183[[http://api.call-cc.org/doc/chicken/modules|modules]] disappear due to compiler optimizations.
184
185
186==== {{nrepl}} on Android
187{{nrepl}} has been used successfully on Android target hardware for
188remote interactive development.  Check
189out [[https://github.com/chicken-mobile/chicken-android-template|this]]
190Android example project.
191
192
193=== Source code repository
194You can find the
195source [[https://github.com/Adellica/chicken-nrepl|here]].
196
197
198=== Author
199Kristian Lein-Mathisen at [[https://github.com/Adellica/|Adellica]]
200
201
202=== License
203BSD
204
Note: See TracBrowser for help on using the repository browser.