Browse code

Complete rewrite. Now functional, and significantly better! ☆

Jaidyn Levesque authored on 2019-07-10 04:22:02
Showing 13 changed files
1 1
similarity index 100%
2 2
rename from COPYING
3 3
rename to COPYING.txt
4 4
deleted file mode 100644
... ...
@@ -1,28 +0,0 @@
1
-================================================================================
2
-RSSS : `RSSS SAGE`	An RSS parser~
3
-================================================================================
4
-
5
-rsss is made to make your life (hopefully) a bit easier when you've got to deal
6
-with RSS data.
7
-
8
-It has functions for easily parsing both the overarching stucture, and the
9
-individual <item>'s.
10
-
11
-
12
-USAGE
13
-
14
-You're probably interested in `rsss:feed-items`, which returns a list of every
15
-item in the RSS feed, and the `rsss:title *data*`, `rsss:description`, etc.,
16
-which fetch the traits of individual <item>'s or the overarching <channel>.
17
-
18
-They're called like so: (function *data*), where *data* is the XML data.
19
-
20
-
21
-BORING STUFF
22
-License is in COPYING (GNU GPLv3)
23
-Author is Jaidyn Ann <jadedctrl@teknik.io>
24
-Sauce is at https://git.eunichx.us/rsss
25 0
new file mode 100644
... ...
@@ -0,0 +1,44 @@
1
+===============================================================================
2
+RSSS                                                        An RSS/Atom parser
3
+===============================================================================
4
+
5
+Reading Syndicated Stuff Sagely is made to make your life (hopefully) a bit
6
+easier when you've got to deal with Atom/RSS feed XML, especially when you need
7
+to handle *both* types of feed.
8
+
9
+It generalizes all types of feed (Atom, RSS 2.0/1.0) into a single RSSS:FEED
10
+object, with subsequent RSSS:ENTRY objects inside for <entry>/<item>s within
11
+the feed.
12
+
13
+
14
+————————————————————————————————————————
15
+USAGE
16
+————————————————————————————————————————
17
+You can turn a feed's XML (string) into an RSSS:FEED object with #'rsss:parse.
18
+
19
+Then, you can read it by means of it's slots.
20
+
21
+Slots of both FEEDs and ENTRYs:
22
+* name
23
+* date
24
+* desc
25
+* uri
26
+
27
+Slots exclusively for FEEDs:
28
+* entries
29
+
30
+Slots exclusively for ENTRYs:
31
+* author
32
+* media
33
+* text
34
+
35
+Each slot has an accessor in the :rsss package, like #'rsss:media, etc.
36
+Good luck!
37
+
38
+
39
+————————————————————————————————————————
40
+BORING STUFF
41
+————————————————————————————————————————
42
+License is in COPYING.txt (GNU GPLv3)
43
+Author is Jaidyn Ann <jadedctrl@teknik.io>
44
+Sauce is at https://git.eunichx.us/rsss.git
... ...
@@ -1,14 +1,9 @@
1 1
 (defsystem "rsss"
2
-  :version "0.1"
2
+  :version "0.9"
3 3
   :author "Jaidyn Ann <jadedctrl@teknik.io>"
4 4
   :license "GPLv3"
5 5
   :depends-on ("xmls")
6
-  :components ((:module "src"
7
-                :components
8
-		((:file "package")
9
-		(:file "main"))))
6
+  :components ((:file "rsss"))
10 7
   :description
11
-	"RSS parser library.")
12
-
13
-
14
-
8
+	"Reading Syndicated Stuff Sagely is a feed format-neutral parser.
9
+	Both Atom and RSS-friendly.")
15 10
new file mode 100644
... ...
@@ -0,0 +1,234 @@
1
+;; This program is free software: you can redistribute it and/or modify it
2
+;; under the terms of the GNU General Public License as published by the
3
+;; Free Software Foundation, either version 3 of the License, or (at your
4
+;; option) any later version.
5
+
6
+;; This program is distributed in the hope that it will be useful, but WITHOUT
7
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8
+;; FITNESS FOR A PARTICULAR PURPOSE.
9
+
10
+;; See the GNU General Public License for more details.
11
+
12
+;; -----------------
13
+
14
+(defpackage :rsss
15
+  (:use :cl)
16
+  (:export 
17
+    :parse
18
+    :entries :name :uri :date :desc :author :text :media))
19
+
20
+(in-package :rsss)
21
+
22
+
23
+;; —————————————————————————————————————
24
+;; CLASSES
25
+;; —————————————————————————————————————
26
+(defclass feed ()
27
+  ((uri     :initarg :uri     :accessor uri     :initform  nil)
28
+   (name    :initarg :name    :accessor name    :initform  nil)
29
+   (date    :initarg :date    :accessor date    :initform  nil)
30
+   (desc    :initarg :desc    :accessor desc    :initform  nil)
31
+   (entries :initarg :entries :accessor entries :initform  nil)))
32
+
33
+(defclass entry ()
34
+  ((uri     :initarg :uri     :accessor uri     :initform  nil)
35
+   (name    :initarg :name    :accessor name    :initform  nil)
36
+   (date    :initarg :date    :accessor date    :initform  nil)
37
+   (author  :initarg :author  :accessor author  :initform  nil)
38
+   (desc    :initarg :desc    :accessor desc    :initform  nil)
39
+   (media   :initarg :media   :accessor media   :initform  nil)
40
+   (text    :initarg :text    :accessor text    :initform  nil)))
41
+
42
+
43
+
44
+;; —————————————————————————————————————
45
+;; MISC
46
+;; —————————————————————————————————————
47
+(defmacro append-or-replace (list item)
48
+  "If a list is empty (nil), then replace it with a new list containing item.
49
+  Otherwise, append item to the pre-existing list.
50
+  Side-effectively, with nconc et. al."
51
+  `(if (nilp ,list)
52
+    (setf ,list  (list ,item))
53
+    (nconc ,list (list ,item))))
54
+
55
+;; VARYING LIST → LIST
56
+(defun equ-assoc (item list)
57
+  "Run #'assoc, but with #'equal as the test function."
58
+  (assoc item list :test #'equal))
59
+
60
+;; VARYING → BOOLEAN
61
+(defun nilp (item)
62
+  "Return whether or note an item is eq to NIL."
63
+  (eq nil item))
64
+
65
+;; LIST → VARYING
66
+(defun ie-car (item)
67
+  "Try car'ing something… but don't sweat it if, y'know, it fucks."
68
+  (ignore-errors (car item)))
69
+
70
+
71
+
72
+;; —————————————————————————————————————
73
+;; PARSING
74
+;; —————————————————————————————————————
75
+;; STRING → RSSS:FEED
76
+(defun parse (xml)
77
+  "Parse a given XML string (atom/rss[12]) into a FEED object."
78
+  (let* ((node (xmls:parse xml))
79
+	 (type (feed-type node)))
80
+    (cond ((or (eq type :rss2) (eq type :rss1))
81
+	   (parse-rss node))
82
+	  ((eq type :atom)
83
+	   (parse-atom node)))))
84
+
85
+;; -----------------
86
+
87
+(defmacro common-let (node extra-let form &optional extra-form)
88
+  "A let-statement used by basically every parsing-function/macro."
89
+  `(let ((name    (xmls:node-name     ,node))
90
+	 (chchild (xmls:node-children ,node))
91
+	 (attrs   (xmls:node-attrs    ,node))
92
+	 ,@extra-let)
93
+     ,form))
94
+
95
+
96
+
97
+;; —————————————————
98
+;; ATOM PARSING
99
+;; —————————————————
100
+(defmacro parse-atom-children (rsss parent-node child-node extra-cond)
101
+  "Code common to parsing both overarching Atom XML and individual entries."
102
+   `(mapcar
103
+      (lambda (,child-node)
104
+	(common-let ,child-node nil
105
+	  (cond ((equal "link"  name)
106
+		 (setf (uri ,rsss)       (cadr (equ-assoc "href" attrs))))
107
+		((equal "title" name)
108
+		 (setf (name ,rsss)      (car chchild)))
109
+		((equal "updated" name)
110
+		 (setf (date ,rsss)      (car chchild)))
111
+		((equal "summary" name)
112
+		 (setf (desc ,rsss)      (car chchild)))
113
+		,extra-cond)))
114
+;;	  nil))
115
+      (xmls:node-children ,parent-node)))
116
+
117
+;; -----------------
118
+
119
+;; XMLS:NODE → RSSS:FEED
120
+(defun parse-atom (atom-node)
121
+  "Parse Atom XMLS node into an rsss FEED object."
122
+  (let ((feed (make-instance 'feed)))
123
+    (parse-atom-children
124
+      feed atom-node atom-child
125
+      ((equal "entry" name)
126
+       (append-or-replace (entries feed) (parse-atom-entry atom-child))))
127
+    feed))
128
+
129
+;; XMLS:NODE → RSSS:ENTRY
130
+(defun parse-atom-entry (entry-node)
131
+  "Parse an Atom <entry>'s XMLS:NODE into an RSSS:ENTRY object."
132
+  (let ((entry (make-instance 'entry)))
133
+    (parse-atom-children
134
+      entry entry-node entry-child
135
+      ((equal "content" name)
136
+       (setf (text entry) (car chchild))))
137
+    entry))
138
+
139
+
140
+
141
+;; —————————————————
142
+;; RSS1/RSS2 PARSING
143
+;; —————————————————
144
+(defmacro parse-rss-children (rsss parent-node child-node
145
+				    &optional (cond-1 `(T nil))
146
+					      (cond-2 `(T nil))
147
+					      (cond-3 `(T nil))
148
+					      (cond-4 `(T nil)))
149
+  "Some code common to parsing the children of rss nodes."
150
+  `(mapcar
151
+     (lambda (,child-node)
152
+       (common-let ,child-node nil
153
+         (cond ((equal "title" name)
154
+		(setf (name ,rsss) (ie-car chchild)))
155
+	       ((equal "pubDate" name)
156
+		(setf (date ,rsss) (ie-car chchild)))
157
+	       ((equal "date" name)
158
+		(setf (date ,rsss) (ie-car chchild)))
159
+	       ((equal "link" name)
160
+		(setf (uri ,rsss)  (ie-car chchild)))
161
+	       ,cond-1 ,cond-2 ,cond-3 ,cond-4)))
162
+     (xmls:node-children ,parent-node)))
163
+
164
+;; -----------------
165
+
166
+;; XMLS:NODE → RSSS:FEED
167
+(defun parse-rss (rss-node)
168
+  "Parse an RSS XMLS node into an rsss:FEED object."
169
+  (let ((feed (make-instance 'feed)))
170
+    (mapcar 
171
+      (lambda (rss-child)
172
+	(let ((name (xmls:node-name rss-child)))
173
+	  (cond ((equal "channel" name)
174
+		 (parse-rss-channel feed rss-child))
175
+		((equal "item"    name)
176
+		 (append-or-replace
177
+		   (entries feed) (parse-rss-item rss-child))))))
178
+      (xmls:node-children rss-node))
179
+    feed))
180
+
181
+;; RSSS:FEED XMLS:NODE → NIL
182
+(defun parse-rss-channel (feed channel-node)
183
+  "Parse a channel node of an RSS feed; modifies the FEED object."
184
+  (parse-rss-children
185
+    feed channel-node channel-child
186
+    ((equal "description" name)
187
+     (setf (desc feed) (ie-car chchild)))
188
+    ((equal "item" name)
189
+     (append-or-replace (entries feed) (parse-rss-item channel-child))))
190
+  feed)
191
+
192
+;; XMLS:NODE → RSSS:ENTRY
193
+(defun parse-rss-item (entry-node)
194
+  "Parse an item (XMLS:NODE) of an RSS feed."
195
+  (let ((entry (make-instance 'entry)))
196
+    (parse-rss-children
197
+      entry entry-node entry-child
198
+      ((or (equal "content" name) (equal "encoded" name))
199
+       (setf (text entry)   (ie-car chchild)))
200
+      ;; about the following: people use <description> tags for both summaries
201
+      ;; and for actual post-bodies. (wtf :/)
202
+      ;; so, if the text is longer than 250 characters, it's *probably* not
203
+      ;; a summary, but an actual post. then again, not all posts are *that*
204
+      ;; long…
205
+      ;; this is a hack that won't always be helpful or effective, it's the
206
+      ;; best trade-off I could think of. sorry ♥
207
+      ((equal "description" name)
208
+       (if (and (< 250 (length (ie-car chchild))) (not (text entry)))
209
+	 (setf (text entry) (ie-car chchild))
210
+	 (setf (desc entry) (ie-car chchild))))
211
+      ((equal "enclosure" name)
212
+       (setf (media entry)  (cadr (assoc "url" attrs :test #'equal))))
213
+      ((or (equal "author" name) (equal "creator" name))
214
+       (setf (author entry) (ie-car chchild))))
215
+    entry))
216
+
217
+
218
+
219
+;; —————————————————————————————————————
220
+;; PRE-PARSING
221
+;; —————————————————————————————————————
222
+;; STRING → SYMBOL
223
+(defun feed-type (node)
224
+  "Return the type of the feed-- :rss2 for RSS 2.0, :rss1 for RSS 1.0/other,
225
+  and :atom for (obviously!) Atom."
226
+  (let ((name    (xmls:node-name node))
227
+	(attrs   (xmls:node-attrs node)))
228
+    (cond ((and (equal "rss" name)
229
+		(equal "2.0" (cadr (assoc "version" attrs :test #'equal))))
230
+	   :rss2)
231
+	  ((equal "rss" name)
232
+	   :rss1)
233
+	  ((equal "feed" name)
234
+	   :atom))))
0 235
deleted file mode 100644
1 236
Binary files a/src/.lib.lisp.swp and /dev/null differ
2 237
deleted file mode 100644
... ...
@@ -1,111 +0,0 @@
1
-(in-package :rsss)
2
-
3
-;; ---------------------------------------- 
4
-;; FEED PARSING
5
-;; ---------------------------------------- 
6
-
7
-(defun feed-values (data value)
8
-  "Return all values from a feed matching a set query."
9
-
10
-  (if (getf-string data "channel")
11
-    (getf-strings (getf-string data "channel") value)
12
-    (getf-strings data value)))
13
-
14
-
15
-
16
-(defun feed-value (data value)
17
-  "Return the first value from a feed matching a set query."
18
-
19
-  (car (feed-values data value)))
20
-
21
-
22
-
23
-(defun feed-value-listless (data value)
24
-  "Return the first value from a feed matching a set query,
25
-  but as an isolated string."
26
-
27
-  (let ((result (feed-value data value)))
28
-
29
-    (if (listp result)
30
-      (car (last result))
31
-      result)))
32
-
33
-
34
-
35
-
36
-(defun feed-items (data)
37
-  "Return a list of all RSS `<item>`s (articles)."
38
-
39
-  (feed-values data "item"))
40
-
41
-
42
-
43
-(defun title (data)
44
-  "Return the title of a set of data.
45
-  Accepts an entire XML file (for the `<channel>`'s data)
46
-  or a single `<item>`."
47
-
48
-  (feed-value-listless data "title"))
49
-
50
-
51
-
52
-(defun description (data)
53
-  "Return the description of a set of data.
54
-  Accepts an entire XML file (for the `<channel>`'s data)
55
-  or a single `<item>`."
56
-
57
-  (feed-value-listless data "description"))
58
-
59
-
60
-
61
-(defun link (data)
62
-  "Return the link of a set of data.
63
-  Accepts an entire XML file (for the `<channel>`'s data)
64
-  or a single `<item>`."
65
-
66
-  (feed-value-listless data "link"))
67
-
68
-
69
-
70
-(defun pubdate (data)
71
-  "Return the publish-date of a set of data.
72
-  Accepts an entire XML file (for the `<channel>`'s data)
73
-  or a single `<item>`."
74
-
75
-  (feed-value-listless data "pubDate"))
76
-
77
-
78
-
79
-
80
-;; ---------------------------------------- 
81
-;; MISC
82
-;; ---------------------------------------- 
83
-
84
-(defun getf-string (list string)
85
-  "Get an item from a list by an identifying string in `car`.
86
-  I.E., if the string is 'apple', the first sublist like this:
87
-  ('apple' 1 2 3)
88
-  will be returned."
89
-
90
-  (car (getf-strings list string)))
91
-
92
-
93
-
94
-(defun getf-strings (list string &optional (stack '()))
95
-  "Get items from list by an identifying string in `car`.
96
-  I.E., if the string is 'apple', any sublists like this:
97
-  ('apple' 1 2 3)
98
-  will be returned."
99
-
100
-  ;; just recurse through the list, adding each new matching
101
-  ;; item to the `stack`
102
-
103
-  (if (and (< 0 (length list)) (listp list))
104
-    (if (ignore-errors     
105
-          ;; the item might not be a list; for our purposes, let's ignore that.
106
-          (equal
107
-            (car (car list))    ;; '( ( here ) )
108
-            string))
109
-      (getf-strings (cdr list) string (concatenate 'list stack (list (car list))))
110
-      (getf-strings (cdr list) string stack))
111
-    stack))
112 0
deleted file mode 100644
... ...
@@ -1,16 +0,0 @@
1
-(defpackage :rsss
2
-  (:use :cl)
3
-  (:export 
4
-    :feed-value
5
-    :feed-values
6
-    :feed-value-listless
7
-
8
-    :feed-items
9
-
10
-    :title
11
-    :description
12
-    :pubdate
13
-    :link
14
-    ))	
15
-
16
-(in-package :rsss)
17 0
similarity index 100%
18 1
rename from t/README
19 2
rename to t/README.txt
20 3
new file mode 100644
... ...
@@ -0,0 +1,4717 @@
1
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
2
+<feed xmlns="http://www.w3.org/2005/Atom">
3
+
4
+  <title>Planet GNU</title>
5
+  <link rel="self" href="https://planet.gnu.org/atom.xml"/>
6
+  <link href="https://planet.gnu.org/"/>
7
+  <id>https://planet.gnu.org/atom.xml</id>
8
+  <updated>2019-07-09T17:55:32+00:00</updated>
9
+  <generator uri="http://intertwingly.net/code/venus/">http://intertwingly.net/code/venus/</generator>
10
+
11
+
12
+  <entry xml:lang="en">
13
+	<title type="html" xml:lang="en">June 2019: Photos from Brno</title>
14
+	<link href="http://www.fsf.org/blogs/rms/photo-blog-2019-june-brno"/>
15
+	<id>http://www.fsf.org/blogs/rms/photo-blog-2019-june-brno</id>
16
+	<updated>2019-07-09T15:39:21+00:00</updated>
17
+	<summary type="html" xml:lang="en"></summary>
18
+	<content type="html" xml:lang="en">&lt;div style=&quot;border-style: solid; border-width: 2px; text-align: left; margin: 4px 50px 4px 50px; padding-top: 4px; padding-left: 5px; padding-right: 5px;&quot;&gt;
19
+
20
+&lt;p&gt;Free Software Foundation president Richard Stallman (RMS) was in
21
+&lt;strong&gt;Brno, Czech Republic&lt;/strong&gt; on June 6, 2019, to give two speeches.&lt;/p&gt;
22
+
23
+&lt;p&gt;In the morning, he took part in the &lt;a href=&quot;https://www.smartcityfair.cz/en/&quot;&gt;URBIS Smart City Fair&lt;/a&gt;, at the Brno Fair Grounds, giving his speech
24
+&quot;Computing, freedom, and privacy.&quot;&lt;sup style=&quot;font-size: 8px;&quot;&gt;&lt;a href=&quot;https://static.fsf.org/fsforg/rss/blogs.xml#fn1&quot; id=&quot;ref1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
25
+
26
+
27
+
28
+
29
+&lt;p style=&quot;text-align: center;&quot;&gt;
30
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-fair-grounds/20190606-brno-c-2019-veletrhy-brno-a-s-cc-by-4-0-1-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
31
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-fair-grounds/20190606-brno-c-2019-veletrhy-brno-a-s-cc-by-4-0-2-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
32
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-fair-grounds/20190606-brno-c-2019-veletrhy-brno-a-s-cc-by-4-0-3-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
33
+&lt;/p&gt;
34
+
35
+ &lt;p style=&quot;padding: 0px 20px 0px 20px; margin-top: 1px; text-align: center;&quot;&gt; 
36
+&lt;em&gt;(Copyright © 2019 Veletrhy Brno, a. s.  Photos licensed under &lt;a href=&quot;http://creativecommons.org/licenses/by/4.0/&quot;&gt;CC BY 4.0&lt;/a&gt;.)&lt;/em&gt;
37
+
38
+&lt;br style=&quot;margin-bottom: 15px;&quot; /&gt;
39
+
40
+&lt;/p&gt;&lt;p&gt;In the afternoon, at the University Cinema Scala, he gave his
41
+speech &quot;The free software movement and the GNU/Linux operating
42
+system,&quot; to about three hundred people.
43
+
44
+
45
+
46
+
47
+&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;
48
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-jan-prokopius-cc-by-4-0-1-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
49
+
50
+ &lt;/p&gt;&lt;p style=&quot;padding: 0px 20px 0px 20px; margin-top: 1px; text-align: center;&quot;&gt; 
51
+&lt;em&gt;(Copyright © 2019 Pavel Loutocký. Photos licensed under &lt;a href=&quot;http://creativecommons.org/licenses/by/4.0/&quot;&gt;CC BY 4.0&lt;/a&gt;.)&lt;/em&gt;
52
+
53
+&lt;br style=&quot;margin-bottom: 15px;&quot; /&gt;
54
+
55
+
56
+
57
+
58
+&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;
59
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-047-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
60
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-050-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
61
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-051-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
62
+
63
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-052-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
64
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-053-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
65
+
66
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-054-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
67
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-055-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
68
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-056-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
69
+
70
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-057-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
71
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-058-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
72
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-064-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
73
+
74
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-063-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
75
+
76
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-066-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
77
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-069-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
78
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-070-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
79
+
80
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-071-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
81
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-073-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
82
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-074-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
83
+
84
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-068-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
85
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-072-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
86
+
87
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-075-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
88
+
89
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-076-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
90
+
91
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-078-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
92
+
93
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-079-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
94
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-080-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
95
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-081-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
96
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-082-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
97
+
98
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-083-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
99
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-084-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
100
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-085-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
101
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-086-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
102
+
103
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-087-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
104
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-088-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
105
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-090-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
106
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-091-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
107
+
108
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-092-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
109
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-093-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
110
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-094-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;133&quot; /&gt;
111
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-095-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;132&quot; /&gt;
112
+
113
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-114-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
114
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-115-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
115
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-116-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
116
+
117
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-117-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
118
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-120-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;265&quot; /&gt;
119
+
120
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-121-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
121
+
122
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-124-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
123
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-126-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
124
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-128-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
125
+
126
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-132-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
127
+
128
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-131-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
129
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-133-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
130
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-134-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
131
+
132
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-135-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
133
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-136-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
134
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-140-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
135
+
136
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-137-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
137
+
138
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-141-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
139
+
140
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-143-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-145-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
141
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-146-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;176&quot; /&gt;
142
+&lt;img alt=&quot;&quot; src=&quot;https://static.fsf.org/static/nosvn/rms-photos/20190606-brno-scala/20190606-brno-c-2019-pavel-loutocky-cc-by-4-0-144-thumb.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;530&quot; /&gt;
143
+
144
+&lt;/p&gt;
145
+
146
+ &lt;p style=&quot;padding: 0px 20px 0px 20px; margin-top: 1px; text-align: center;&quot;&gt; 
147
+&lt;em&gt;(Copyright © 2019 Pavel Loutocký. Photos licensed under &lt;a href=&quot;http://creativecommons.org/licenses/by/4.0/&quot;&gt;CC BY 4.0&lt;/a&gt;.)&lt;/em&gt;
148
+
149
+&lt;br style=&quot;margin-bottom: 15px;&quot; /&gt;
150
+
151
+&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;Thank you to everyone who made this visit possible!&lt;/strong&gt;&lt;/p&gt;
152
+
153
+
154
+
155
+&lt;p style=&quot;text-align: center;&quot;&gt;If you&#39;re in the area, please fill out our contact form, so
156
+that we can inform you about future events in and around &lt;a href=&quot;https://my.fsf.org/civicrm/profile/create?gid=578&amp;amp;reset=1&quot;&gt;Brno&lt;/a&gt;.
157
+&lt;/p&gt;
158
+
159
+&lt;p style=&quot;text-align: center;&quot;&gt;Please see &lt;a href=&quot;http://www.fsf.org/events&quot;&gt;www.fsf.org/events&lt;/a&gt; for a full
160
+list of all of RMS&#39;s confirmed engagements, &lt;br /&gt; and contact &lt;a href=&quot;mailto:rms-assist@gnu.org&quot;&gt;rms-assist@gnu.org&lt;/a&gt; if you&#39;d like
161
+him to come speak.&lt;/p&gt;
162
+
163
+&lt;hr /&gt;
164
+
165
+&lt;sup id=&quot;fn1&quot;&gt;1. The recording will soon be posted on &lt;a href=&quot;http://audio-video.gnu.org&quot;&gt;our audio-video archive&lt;/a&gt;.&lt;a href=&quot;https://static.fsf.org/fsforg/rss/blogs.xml#ref1&quot; title=&quot;Jump back to footnote 1 in the text.&quot;&gt;↩&lt;/a&gt;&lt;/sup&gt;&lt;br /&gt;&lt;/div&gt;</content>
166
+	<author>
167
+	  <name>FSF Blogs</name>
168
+	   <uri>http://www.fsf.org/blogs/recent-blog-posts</uri> 
169
+	</author>
170
+	<source>
171
+	  <title type="html">FSF blogs</title>
172
+	  <subtitle type="html">Writing by representatives of the Free Software Foundation.</subtitle>
173
+	  <link rel="self" href="http://static.fsf.org/fsforg/rss/blogs.xml"/>
174
+	  <id>http://www.fsf.org/blogs/recent-blog-posts</id>  
175
+	</source>
176
+  </entry>
177
+  
178
+  <entry xml:lang="en">
179
+	<title type="html" xml:lang="en">Racket is an acceptable Python</title>
180
+	<link href="http://dustycloud.org/blog/racket-is-an-acceptable-python/"/>
181
+	<id>tag:dustycloud.org,2019-07-09:blog/racket-is-an-acceptable-python/</id>
182
+	<updated>2019-07-09T14:27:00+00:00</updated>
183
+	<summary type="html" xml:lang="en"></summary>
184
+	<content type="html" xml:lang="en">&lt;p&gt;A little over a decade ago, there were some popular blogposts about whether
185
+&lt;a class=&quot;reference external&quot; href=&quot;http://www.randomhacks.net/2005/12/03/why-ruby-is-an-acceptable-lisp/&quot;&gt;Ruby was an acceptable Lisp&lt;/a&gt;
186
+or whether even
187
+&lt;a class=&quot;reference external&quot; href=&quot;https://steve-yegge.blogspot.com/2006/04/lisp-is-not-acceptable-lisp.html&quot;&gt;Lisp was an acceptable Lisp&lt;/a&gt;.
188
+Peter Norvig was also writing at the time
189
+&lt;a class=&quot;reference external&quot; href=&quot;https://norvig.com/python-lisp.html&quot;&gt;introducing Python to Lisp programmers&lt;/a&gt;.
190
+Lisp, those in the know knew, was the right thing to strive for, and yet
191
+seemed unattainable for anything aimed for production since the AI Winter
192
+shattered Lisp&#39;s popularity in the 80s/early 90s.
193
+If you can&#39;t get Lisp, what&#39;s closest thing you can get?&lt;/p&gt;
194
+&lt;p&gt;This was around the time I was starting to program; I had spent some
195
+time configuring my editor with Emacs Lisp and loved every moment I
196
+got to do it; I read some Lisp books and longed for more.
197
+And yet when I tried to &quot;get things done&quot; in the language, I just
198
+couldn&#39;t make as much headway as I could with my preferred language
199
+for practical projects at the time: Python.&lt;/p&gt;
200
+&lt;p&gt;Python was great... mostly.
201
+It was easy to read, it was easy to write, it was easy-ish to teach
202
+to newcomers.
203
+(Python&#39;s intro material is better than most, but &lt;a class=&quot;reference external&quot; href=&quot;https://mlemmer.org/&quot;&gt;my spouse&lt;/a&gt; has talked before about some major pitfalls
204
+that the Python documentation has which make getting started
205
+unnecessarily hard.
206
+You can
207
+&lt;a class=&quot;reference external&quot; href=&quot;https://www.youtube.com/watch?v=pv0lLciMI24&amp;amp;t=4220s&quot;&gt;hear her talk about that&lt;/a&gt;
208
+at this talk we co-presented on at last year&#39;s RacketCon.)
209
+I ran a large free software project on a Python codebase, and it was
210
+easy to get new contributors; the barrier to entry to becoming a
211
+programmer with Python was low.
212
+I consider that to be a feature, and it certainly helped me bootstrap
213
+my career.&lt;/p&gt;
214
+&lt;p&gt;Most importantly of all though, Python was easy to pick up and run with
215
+because no matter what you wanted to do, either the tools came built
216
+in or the Python ecosystem had enough of the pieces nearby that building
217
+what you wanted was usually fairly trivial.&lt;/p&gt;
218
+&lt;p&gt;But Python has its limitations, and I always longed for a lisp.
219
+For a brief time, I thought I could get there by contributing to the
220
+&lt;a class=&quot;reference external&quot; href=&quot;https://hy.readthedocs.io/en/stable/&quot;&gt;Hy project&lt;/a&gt;, which was a lisp
221
+that transformed itself into the Python AST.
222
+&quot;Why write Python in a syntax that&#39;s easy to read when you could add
223
+a bunch of parentheses to it instead?&quot; I would joke when I talked about
224
+it.
225
+Believe it or not though, I do consider lisps easier to read, once you
226
+are comfortable to understand their syntax.
227
+I certainly find them easier to write and modify.
228
+And I longed for the metaprogramming aspects of Lisp.&lt;/p&gt;
229
+&lt;p&gt;Alas, Hy didn&#39;t really reach my dream.
230
+That macro expansion made debugging a nightmare as Hy would lose track
231
+of where the line numbers are; it wasn&#39;t until that when I really realized
232
+that without line numbers, you&#39;re just lost in terms of debugging in
233
+Python-land.
234
+That and Python didn&#39;t really have the right primitives; immutable
235
+datastructures for whatever reason never became first class, meaning
236
+that functional programming was hard, &quot;cons&quot; didn&#39;t really exist
237
+(actually this doesn&#39;t matter as much as people might think), recursive
238
+programming isn&#39;t really as possible without tail call elimination, etc
239
+etc etc.&lt;/p&gt;
240
+&lt;p&gt;But I missed parentheses.
241
+I longed for parentheses.
242
+I &lt;em&gt;dreamed in&lt;/em&gt; parentheses.
243
+I&#39;m not kidding, the only dreams I&#39;ve ever had in code were in lisp,
244
+and it&#39;s happened multiple times, programs unfolding before me.
245
+The structure of lisp makes the flow of code so clear, and there&#39;s
246
+simply nothing like the comfort of developing in front of a lisp REPL.&lt;/p&gt;
247
+&lt;p&gt;Yet to choose to use a lisp seemed to mean opening myself up to
248
+eternal yak-shaving of developing packages that were already available
249
+on the Python Package Index or limiting my development community an
250
+elite group of Emacs users.
251
+When I was in Python, I longed for the beauty of a Lisp; when I was in
252
+a Lisp, I longed for the ease of Python.&lt;/p&gt;
253
+&lt;p&gt;All this changed when I discovered Racket:&lt;/p&gt;
254
+&lt;ul class=&quot;simple&quot;&gt;
255
+&lt;li&gt;Racket comes with a full-featured editor named DrRacket built-in
256
+that&#39;s damn nice to use.
257
+It has all the features that make lisp hacking comfortable previously
258
+mostly only to Emacs users: parenthesis balancing, comfortable REPL
259
+integration, etc etc.
260
+But if you want to use Emacs, you can use racket-mode.
261
+Win-win.&lt;/li&gt;
262
+&lt;li&gt;Racket has intentionally been built as an educational language, not
263
+unlike Python.
264
+One of the core audiences of Racket is middle schoolers, and it even
265
+comes with a built-in game engine for kids.&lt;/li&gt;
266
+&lt;li&gt;My spouse and I even taught classes about how to learn to program for
267
+&lt;a class=&quot;reference external&quot; href=&quot;https://dustycloud.org/misc/digital-humanities/&quot;&gt;humanities academics&lt;/a&gt;
268
+using Racket.
269
+We found the age-old belief that &quot;lisp syntax is just too hard&quot; is
270
+simply false; the main thing that most people lack is decent lisp-friendly
271
+tooling with a low barrier to entry, and DrRacket provides that.
272
+The only people who were afraid of the parentheses turned out to be people
273
+who already knew how to program.
274
+Those who didn&#39;t even praised the syntax for its clarity and the way
275
+the editor could help show you when you made a syntax error
276
+(DrRacket is very good at that).
277
+&quot;Lisp is too hard to learn&quot; is a lie; if middle schoolers can learn it,
278
+so can more seasoned programmers.&lt;/li&gt;
279
+&lt;li&gt;Racket might even be &lt;em&gt;more&lt;/em&gt; batteries included than Python.
280
+At least all the batteries that come included are generally nicer;
281
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/gui/&quot;&gt;Racket&#39;s GUI library&lt;/a&gt; is the only
282
+time I&#39;ve ever had fun in my life writing GUI programs (and they&#39;re cross
283
+platform too).
284
+Constructing pictures with its &lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/pict/index.html&quot;&gt;pict&lt;/a&gt;
285
+library is a delight.
286
+Plotting graphs with &lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/plot/index.html&quot;&gt;plot&lt;/a&gt;
287
+is an incredible experience.
288
+Writing documentation with
289
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/scribble/index.html&quot;&gt;Scribble&lt;/a&gt;
290
+is the best non-org-mode experience I&#39;ve ever had, but has the
291
+advantage over org-mode in that your document is just inverted code.
292
+I could go on.
293
+And these are just some packages bundled with Racket; the
294
+&lt;a class=&quot;reference external&quot; href=&quot;https://pkgs.racket-lang.org&quot;&gt;Package repository&lt;/a&gt; contains much more.&lt;/li&gt;
295
+&lt;li&gt;Racket&#39;s documentation is, in my experience, unparalleled.
296
+The &lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/guide/index.html&quot;&gt;Racket Guide&lt;/a&gt; walks
297
+you through all the key concepts, and the
298
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/reference/index.html&quot;&gt;Racket Reference&lt;/a&gt;
299
+has everything else you need.&lt;/li&gt;
300
+&lt;li&gt;The tutorials are also wonderful; the
301
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/quick/index.html&quot;&gt;introductory tutorial&lt;/a&gt;
302
+gets your feet wet not through composing numbers or strings but by
303
+building up pictures.
304
+Want to learn more?
305
+The next two tutorials show you how to
306
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/continue/index.html&quot;&gt;build web applications&lt;/a&gt;
307
+and then
308
+&lt;a class=&quot;reference external&quot; href=&quot;https://docs.racket-lang.org/more/index.html&quot;&gt;build your own web server&lt;/a&gt;.&lt;/li&gt;
309
+&lt;li&gt;Like Python, even though Racket has its roots in education, it is more than
310
+ready for serious practical use.
311
+These days, when I want to build something and get it done quickly
312
+and efficiently, I reach for Racket first.&lt;/li&gt;
313
+&lt;/ul&gt;
314
+&lt;p&gt;Racket is a great Lisp, but it&#39;s also an acceptable Python.
315
+Sometimes you really can have it all.&lt;/p&gt;</content>
316
+	<author>
317
+	  <name>Christopher Lemmer Webber</name>
318
+	   <uri>http://dustycloud.org/</uri> 
319
+	</author>
320
+	<source>
321
+	  <title type="html">DustyCloud Brainstorms</title>
322
+	  
323
+	  <link rel="self" href="http://dustycloud.org/blog/index.xml"/>
324
+	  <id>http://dustycloud.org/</id>  
325
+	</source>
326
+  </entry>
327
+  
328
+  <entry xml:lang="en">
329
+	<title type="html" xml:lang="en">DW5821e firmware update integration in ModemManager and fwupd</title>
330
+	<link href="https://sigquit.wordpress.com/2019/07/03/dw5821e-firmware-update-integration-in-modemmanager-and-fwupd/"/>
331
+	<id>http://sigquit.wordpress.com/?p=1220</id>
332
+	<updated>2019-07-03T13:27:10+00:00</updated>
333
+	<summary type="html" xml:lang="en"></summary>
334
+	<content type="html" xml:lang="en">&lt;p&gt;&lt;img alt=&quot;&quot; class=&quot;aligncenter size-full wp-image-1222&quot; height=&quot;522&quot; src=&quot;https://sigquit.files.wordpress.com/2019/07/dw5821e-image.png?w=604&amp;amp;h=522&quot; width=&quot;604&quot; /&gt;&lt;/p&gt;
335
+&lt;p&gt;The &lt;strong&gt;Dell Wireless 5821e&lt;/strong&gt; module is a &lt;a href=&quot;https://www.qualcomm.com/products/snapdragon-modems-4g-lte-x20&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Qualcomm SDX20&lt;/a&gt; based LTE Cat16 device. This modem can work in either MBIM mode or QMI mode, and provides different USB layouts for each of the modes. In Linux kernel based and Windows based systems, the MBIM mode is the default one, because it provides easy integration with the OS (e.g. no additional drivers or connection managers required in Windows) and also provides all the features that QMI provides through QMI over MBIM operations.&lt;/p&gt;
336
+&lt;p&gt;The firmware update process of this DW5821e module is integrated in your GNU/Linux distribution, since &lt;a href=&quot;https://lists.freedesktop.org/archives/modemmanager-devel/2019-January/006983.html&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;ModemManager 1.10.0&lt;/a&gt; and &lt;a href=&quot;https://groups.google.com/forum/#!msg/fwupd/HXavD9QqW4Q/WAsKVunxBQAJ&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;fwupd 1.2.6&lt;/a&gt;. There is no official firmware released in the LVFS (yet) but the setup is completely ready to be used, just waiting for Dell to publish an initial official firmware release.&lt;/p&gt;
337
+&lt;p&gt;The firmware update integration between ModemManager and fwupd involves different steps, which I’ll try to describe here so that it’s clear how to add support for more devices in the future.&lt;/p&gt;
338
+&lt;h2&gt;1) ModemManager reports expected update methods, firmware version and device IDs&lt;/h2&gt;
339
+&lt;p&gt;The Firmware interface in the modem object exposed in DBus contains, since MM 1.10, a new &lt;a href=&quot;https://www.freedesktop.org/software/ModemManager/api/latest/gdbus-org.freedesktop.ModemManager1.Modem.Firmware.html#gdbus-property-org-freedesktop-ModemManager1-Modem-Firmware.UpdateSettings&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;UpdateSettings&lt;/a&gt; property that provides a bitmask specifying which is the expected firmware update method (or methods) required for a given module, plus a dictionary of key-value entries specifying settings applicable to each of the update methods.&lt;/p&gt;
340
+&lt;p&gt;In the case of the DW5821e, two update methods are reported in the bitmask: “&lt;strong&gt;fastboot&lt;/strong&gt;” and “&lt;strong&gt;qmi-pdc&lt;/strong&gt;“, because both are required to have a complete firmware upgrade procedure. “fastboot” would be used to perform the system upgrade by using an OTA update file, and “qmi-pdc” would be used to install the per-carrier configuration files after the system upgrade has been done.&lt;/p&gt;
341
+&lt;p&gt;The list of settings provided in the dictionary contain the two mandatory fields required for all devices that support at least one firmware update method: “device-ids” and “version”. These two fields are designed so that fwupd can fully rely on them during its operation:&lt;/p&gt;
342
+&lt;ul&gt;
343
+&lt;li&gt;The “&lt;strong&gt;device-ids&lt;/strong&gt;” field will include a list of strings providing the device IDs associated to the device, sorted from the most specific to the least specific. These device IDs are the ones that fwupd will use to build the GUIDs required to match a given device to a given firmware package. The DW5821e will expose four different device IDs:
344
+&lt;ul&gt;
345
+&lt;li&gt;“USB\&lt;strong&gt;VID_413C&lt;/strong&gt;“: specifying this is a Dell-branded device.&lt;/li&gt;
346
+&lt;li&gt;“USB\VID_413C&amp;amp;&lt;strong&gt;PID_81D7&lt;/strong&gt;“: specifying this is a DW5821e module.&lt;/li&gt;
347
+&lt;li&gt;“USB\VID_413C&amp;amp;PID_81D7&amp;amp;&lt;strong&gt;REV_0318&lt;/strong&gt;“: specifying this is hardware revision 0x318 of the DW5821e module.&lt;/li&gt;
348
+&lt;li&gt;“USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;&lt;strong&gt;CARRIER_VODAFONE&lt;/strong&gt;“: specifying this is hardware revision 0x318 of the DW5821e module running with a Vodafone-specific carrier configuration.&lt;/li&gt;
349
+&lt;/ul&gt;
350
+&lt;/li&gt;
351
+&lt;li&gt;The “&lt;strong&gt;version&lt;/strong&gt;” field will include the firmware version string of the module, using the same format as used in the firmware package files used by fwupd. This requirement is obviously very important, because if the format used is different, the simple version string comparison used by fwupd (literally ASCII string comparison) would not work correctly. It is also worth noting that if the carrier configuration is also versioned, the version string should contain not only the version of the system, but also the version of the carrier configuration. The DW5821e will expose a firmware version including both, e.g. “T77W968.F1.1.1.1.1.VF.001” (system version being F1.1.1.1.1 and carrier config version being “VF.001”)&lt;/li&gt;
352
+&lt;li&gt;In addition to the mandatory fields, the dictionary exposed by the DW5821e will also contain a “&lt;strong&gt;fastboot-at&lt;/strong&gt;” field specifying which AT command can be used to switch the module into fastboot download mode.&lt;/li&gt;
353
+&lt;/ul&gt;
354
+&lt;h2&gt;2) fwupd matches GUIDs and checks available firmware versions&lt;/h2&gt;
355
+&lt;p&gt;Once fwupd detects a modem in ModemManager that is able to expose the correct UpdateSettings property in the Firmware interface, it will add the device as a known device that may be updated in its own records. The device exposed by fwupd will contain the &lt;a href=&quot;https://en.wikipedia.org/wiki/Universally_unique_identifier&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;GUIDs&lt;/strong&gt;&lt;/a&gt; built from the “device-ids” list of strings exposed by ModemManager. E.g. for the “USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;CARRIER_VODAFONE” device ID, fwupd will use GUID “b595e24b-bebb-531b-abeb-620fa2b44045”.&lt;/p&gt;
356
+&lt;p&gt;fwupd will then be able to look for firmware packages (CAB files) available in the &lt;a href=&quot;https://fwupd.org/&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;LVFS&lt;/a&gt; that are associated to any of the GUIDs exposed for the DW5821e.&lt;/p&gt;
357
+&lt;p&gt;The CAB files packaged for the LVFS will contain one single firmware OTA file plus one carrier MCFG file for each supported carrier in the give firmware version. The CAB files will also contain one “metainfo.xml” file for each of the supported carriers in the released package, so that per-carrier firmware upgrade paths are available: only firmware updates for the currently used carrier should be considered. E.g. we don’t want users running with the Vodafone carrier config to get notified of upgrades to newer firmware versions that aren’t certified for the Vodafone carrier.&lt;/p&gt;
358
+&lt;p&gt;Each of the CAB files with multiple “metainfo.xml” files will therefore be associated to multiple GUID/version pairs. E.g. the same CAB file will be valid for the following GUIDs (using Device ID instead of GUID for a clearer explanation, but really the match is per GUID not per Device ID):&lt;/p&gt;
359
+&lt;ul&gt;
360
+&lt;li&gt;Device ID “USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;CARRIER_VODAFONE” providing version “T77W968.F1.2.2.2.2.VF.002”&lt;/li&gt;
361
+&lt;li&gt;Device ID “USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;CARRIER_TELEFONICA” providing version “T77W968.F1.2.2.2.2.TF.003”&lt;/li&gt;
362
+&lt;li&gt;Device ID “USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;CARRIER_VERIZON” providing version “T77W968.F1.2.2.2.2.VZ.004”&lt;/li&gt;
363
+&lt;li&gt;… and so on.&lt;/li&gt;
364
+&lt;/ul&gt;
365
+&lt;p&gt;Following our example, fwupd will detect our device exposing device ID “USB\VID_413C&amp;amp;PID_81D7&amp;amp;REV_0318&amp;amp;CARRIER_VODAFONE” and version “T77W968.F1.1.1.1.1.VF.001” in ModemManager and will be able to find a CAB file for the same device ID providing a newer version “T77W968.F1.2.2.2.2.VF.002” in the LVFS. The firmware update is possible!&lt;/p&gt;
366
+&lt;h2&gt;3) fwupd requests device inhibition from ModemManager&lt;/h2&gt;
367
+&lt;p&gt;In order to perform the firmware upgrade, fwupd requires full control of the modem. Therefore, when the firmware upgrade process starts, fwupd will use the new &lt;a href=&quot;https://www.freedesktop.org/software/ModemManager/api/latest/gdbus-org.freedesktop.ModemManager1.html#gdbus-method-org-freedesktop-ModemManager1.InhibitDevice&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;InhibitDevice&lt;/a&gt;(TRUE) method in the Manager DBus interface of ModemManager to request that a specific modem with a specific uid should be inhibited. Once the device is inhibited in ModemManager, it will be disabled and removed from the list of modems in DBus, and no longer used until the inhibition is removed.&lt;/p&gt;
368
+&lt;p&gt;The inhibition may be removed by calling InhibitDevice(FALSE) explicitly once the firmware upgrade is finished, and will also be automatically removed if the program that requested the inhibition disappears from the bus.&lt;/p&gt;
369
+&lt;h2&gt;4) fwupd downloads CAB file from LVFS and performs firmware update&lt;/h2&gt;
370
+&lt;p&gt;Once the modem is inhibited in ModemManager, fwupd can right away start the firmware update process. In the case of the DW5821e, the firmware update requires two different methods and two different upgrade cycles.&lt;/p&gt;
371
+&lt;p&gt;The first step would be to reboot the module into &lt;strong&gt;fastboot&lt;/strong&gt; download mode using the AT command specified by ModemManager in the “at-fastboot” entry of the “UpdateSettings” property dictionary. After running the AT command, the module will reset itself and reboot with a completely different USB layout (and different vid:pid) that fwupd can detect as being the same device as before but in a different working mode. Once the device is in fastboot mode, fwupd will download and install the OTA file using the fastboot protocol, as defined in the “flashfile.xml” file provided in the CAB file:&lt;/p&gt;
372
+&lt;pre&gt;&amp;lt;parts interface=&quot;AP&quot;&amp;gt;
373
+  &amp;lt;part operation=&quot;flash&quot; partition=&quot;ota&quot; filename=&quot;T77W968.F1.2.2.2.2.AP.123_ota.bin&quot; MD5=&quot;f1adb38b5b0f489c327d71bfb9fdcd12&quot;/&amp;gt;
374
+&amp;lt;/parts&amp;gt;&lt;/pre&gt;
375
+&lt;p&gt;Once the OTA file is completely downloaded and installed, fwupd will trigger a reset of the module also using the fastboot protocol, and the device will boot from scratch on the newly installed firmware version. During this initial boot, the module will report itself running in a “default” configuration not associated to any carrier, because the OTA file update process involves fully removing all installed carrier-specific MCFG files.&lt;/p&gt;
376
+&lt;p&gt;The second upgrade cycle performed by fwupd once the modem is detected again involves downloading all carrier-specific MCFG files one by one into the module using the &lt;strong&gt;QMI PDC&lt;/strong&gt; protocol. Once all are downloaded, fwupd will activate the specific carrier configuration that was previously active before the download was started. E.g. if the module was running with the Vodafone-specific carrier configuration before the upgrade, fwupd will select the Vodafone-specific carrier configuration after the upgrade. The module would be reseted one last time using the QMI DMS protocol as a last step of the upgrade procedure.&lt;/p&gt;
377
+&lt;h2&gt;5) fwupd removes device inhibition from ModemManager&lt;/h2&gt;
378
+&lt;p&gt;The upgrade logic will finish by removing the device inhibition from ModemManager using InhibitDevice(FALSE) explicitly. At that point, ModemManager would re-detect and re-probe the modem from scratch, which should already be running in the newly installed firmware and with the newly selected carrier configuration.&lt;/p&gt;</content>
379
+	<author>
380
+	  <name>aleksander</name>
381
+	   <uri>https://sigquit.wordpress.com</uri> 
382
+	</author>
383
+	<source>
384
+	  <title type="html">GNU Planet – SIGQUIT</title>
385
+	  <subtitle type="html">... and core dumped</subtitle>
386
+	  <link rel="self" href="https://sigquit.wordpress.com/category/planets/gnu-planet/feed/"/>
387
+	  <id>https://sigquit.wordpress.com</id>  
388
+	</source>
389
+  </entry>
390
+  
391
+  <entry xml:lang="en">
392
+	<title type="html" xml:lang="en">Version 2.0</title>
393
+	<link href="http://savannah.gnu.org/forum/forum.php?forum_id=9461"/>
394
+	<id>http://savannah.gnu.org/forum/forum.php?forum_id=9461</id>
395
+	<updated>2019-07-01T08:15:26+00:00</updated>
396
+	<summary type="html" xml:lang="en"></summary>
397
+	<content type="html" xml:lang="en">&lt;p&gt;Version 2.0 is available for download from &lt;a href=&quot;https://ftp.gnu.org/gnu/rush/rush-2.0.tar.xz&quot;&gt;GNU&lt;/a&gt; and &lt;a href=&quot;http://download.gnu.org.ua/release/rush/rush-2.0.tar.xz&quot;&gt;Puszcza&lt;/a&gt; archives.
398
+&lt;br /&gt;
399
+&lt;/p&gt;
400
+&lt;p&gt;This release features a complete rewrite of the configuration support. It introduces a new configuration file syntax that offers a large set of control structures and transformation instructions for handling arbitrary requests.  Please see the documentation for details.
401
+&lt;br /&gt;
402
+&lt;/p&gt;
403
+&lt;p&gt;Backward compatibility with prior releases is retained and old configuration syntax is still supported.  This ensures that existing installations will remain operational without any changes. Nevertheless, system administrators are encouraged to switch to the new syntax as soon as possible.&lt;br /&gt;
404
+&lt;/p&gt;</content>
405
+	<author>
406
+	  <name>Sergey Poznyakoff</name>
407
+	   <uri>http://savannah.gnu.org/news/atom.php?group=rush</uri> 
408
+	</author>
409
+	<source>
410
+	  <title type="html">GNU Rush - News</title>
411
+	  
412
+	  <link rel="self" href="http://savannah.gnu.org/news/atom.php?group=rush"/>
413
+	  <id>http://savannah.gnu.org/news/atom.php?group=rush</id>  
414
+	</source>
415
+  </entry>
416
+  
417
+  <entry xml:lang="en">
418
+	<title type="html" xml:lang="non-html">GNU Guile 2.2.6 released</title>
419
+	<link href="https://www.gnu.org/software/guile/news/gnu-guile-226-released.html"/>
420
+	<id>https://www.gnu.org/software/guile/news/gnu-guile-226-released.html</id>
421
+	<updated>2019-06-30T21:55:00+00:00</updated>
422
+	<summary type="html" xml:lang="non-html"></summary>
423
+	<content type="html" xml:lang="en">&lt;p&gt;We are pleased to announce GNU Guile 2.2.6, the sixth bug-fix
424
+release in the new 2.2 stable release series.  This release represents
425
+11 commits by 4 people since version 2.2.5.  First and foremost, it
426
+fixes a &lt;a href=&quot;https://issues.guix.gnu.org/issue/36350&quot;&gt;regression&lt;/a&gt; introduced
427
+in 2.2.5 that would break Guile’s built-in HTTP server.&lt;/p&gt;&lt;p&gt;See the &lt;a href=&quot;https://lists.gnu.org/archive/html/guile-devel/2019-06/msg00059.html&quot;&gt;release
428
+announcement&lt;/a&gt;
429
+for details.&lt;/p&gt;</content>
430
+	<author>
431
+	  <name>Ludovic Courtès</name>
432
+	   <email>guile-devel@gnu.org</email> 
433
+	</author>
434
+	<source>
435
+	  <title type="html">GNU&#39;s programming and extension language</title>
436
+	  <subtitle type="html">Recent Posts</subtitle>
437
+	  <link rel="self" href="https://www.gnu.org/software/guile/news/feed.xml"/>
438
+	  <id>https://www.gnu.org/software/guile</id>  
439
+	</source>
440
+  </entry>
441
+  
442
+  <entry xml:lang="en">
443
+	<title type="html" xml:lang="en">Malayalam team re-established</title>
444
+	<link href="http://savannah.gnu.org/forum/forum.php?forum_id=9459"/>
445
+	<id>http://savannah.gnu.org/forum/forum.php?forum_id=9459</id>
446
+	<updated>2019-06-29T06:54:45+00:00</updated>
447
+	<summary type="html" xml:lang="en"></summary>
448
+	<content type="html" xml:lang="en">&lt;p&gt;After more than 8 years of being orphaned, Malayalam team is active again.  The new team leader, Aiswarya Kaitheri Kandoth, made a new translation of the &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.html&quot;&gt;Free Software Definition&lt;/a&gt;, so now we have 41 translations of that page!
449
+&lt;br /&gt;
450
+&lt;/p&gt;
451
+&lt;p&gt;Currently, Malayalam the only active translation team of official languages of India.  It is a Dravidian language spoken by about 40 million people worldwide, with the most speakers living in the Indian state of Kerala.  Like many Indian languages, it uses a syllabic script derived from Brahmi.
452
+&lt;br /&gt;
453
+&lt;/p&gt;
454
+&lt;p&gt;&lt;a href=&quot;https://www.gnu.org/software/gnun/reports/report-ml.html#complete&quot;&gt;Links to up-to-date translations&lt;/a&gt; are shown on the automatically generated &lt;a href=&quot;https://www.gnu.org/software/gnun/reports/report-ml.html&quot;&gt;report page&lt;/a&gt;.&lt;br /&gt;
455
+&lt;/p&gt;</content>
456
+	<author>
457
+	  <name>Ineiev</name>
458
+	   <uri>http://savannah.gnu.org/news/atom.php?group=trans-coord</uri> 
459
+	</author>
460
+	<source>
461
+	  <title type="html">GNU Web Translation Coordination - News</title>
462
+	  
463
+	  <link rel="self" href="http://savannah.gnu.org/news/atom.php?group=trans-coord"/>
464
+	  <id>http://savannah.gnu.org/news/atom.php?group=trans-coord</id>  
465
+	</source>
466
+  </entry>
467
+  
468
+  <entry xml:lang="en">
469
+	<title type="html" xml:lang="en">How do Spritely&#39;s actor and storage layers tie together?</title>
470
+	<link href="http://dustycloud.org/blog/how-do-spritelys-actor-and-storage-layers-tie-together/"/>
471
+	<id>tag:dustycloud.org,2019-06-27:blog/how-do-spritelys-actor-and-storage-layers-tie-together/</id>
472
+	<updated>2019-06-27T18:15:00+00:00</updated>
473
+	<summary type="html" xml:lang="en"></summary>
474
+	<content type="html" xml:lang="en">&lt;p&gt;I&#39;ve been hacking away at &lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely&quot;&gt;Spritely&lt;/a&gt;
475
+(see &lt;a class=&quot;reference external&quot; href=&quot;https://dustycloud.org/blog/tag/spritely/&quot;&gt;previously&lt;/a&gt;).
476
+Recently I&#39;ve been making progress on both the actor model
477
+(&lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely/goblins&quot;&gt;goblins&lt;/a&gt; and its rewrite
478
+&lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely/goblinoid&quot;&gt;goblinoid&lt;/a&gt;) as well as
479
+the storage layers (currently called
480
+&lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/dustyweb/magenc/blob/master/magenc/scribblings/intro.org&quot;&gt;Magenc&lt;/a&gt;
481
+and &lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely/crystal/blob/master/crystal/scribblings/intro.org&quot;&gt;Crystal&lt;/a&gt;,
482
+but we are talking about probably renaming the both of them into a
483
+suite called &quot;datashards&quot;... yeah, everything is moving and changing
484
+fast right now.)&lt;/p&gt;
485
+&lt;p&gt;In the &lt;a class=&quot;reference external&quot; href=&quot;https://webchat.freenode.net/?channels=spritely&quot;&gt;#spritely channel on freenode&lt;/a&gt;
486
+a friend asked, what is the big picture idea here?
487
+Both the actor model layer and the storage layer describe themselves
488
+as using &quot;capabilities&quot; (or more precisely &quot;object capabilities&quot; or
489
+&quot;ocaps&quot;) but they seem to be implemented differently.
490
+How does it all tie together?&lt;/p&gt;
491
+&lt;p&gt;A great question!
492
+I think the first point of confusion is that while both follow the
493
+ocap paradigm (which is to say, reference/possession-based authority...
494
+possessing the capability gives you access, and it does not matter what
495
+your identity is for the most part for access control), they are
496
+implemented very differently because they are solving different problems.
497
+The storage system is based on encrypted, persistent data, with its ideas
498
+drawn from &lt;a class=&quot;reference external&quot; href=&quot;https://tahoe-lafs.org/trac/tahoe-lafs&quot;&gt;Tahoe-LAFS&lt;/a&gt; and
499
+&lt;a class=&quot;reference external&quot; href=&quot;https://freenetproject.org/&quot;&gt;Freenet&lt;/a&gt;, and the way that capabilities
500
+work is based on possession of cryptographic keys (which are themselves
501
+embedded/referenced in the URIs).
502
+The actor model, on the other hand, is based on holding onto a
503
+reference to a unique, unguessable URL (well, that&#39;s a bit of an
504
+intentional oversimplification for the sake of this explaination but
505
+we&#39;ll run with it) where the actor at that URL is &quot;live&quot; and communicated
506
+with via message passing.
507
+(Most of the ideas from this come from &lt;a class=&quot;reference external&quot; href=&quot;http://erights.org/&quot;&gt;E&lt;/a&gt; and
508
+&lt;a class=&quot;reference external&quot; href=&quot;http://waterken.sourceforge.net/&quot;&gt;Waterken&lt;/a&gt;.)
509
+Actors are connected to each other over secure channels to prevent
510
+eavesdropping or leakage of the capabilities.&lt;/p&gt;
511
+&lt;p&gt;So yeah, how do these two seemingly very different layers tie together?
512
+As usual, I find that I most easily explain things via narrative, so
513
+let&#39;s imagine the following game scenario: Alice is in a room with a goblin.
514
+First Alice sees the goblin, then Alice attacks the goblin, then the
515
+goblin and Alice realize that they are not so different and become
516
+best friends.&lt;/p&gt;
517
+&lt;p&gt;The goblin and Alice both manifest in this universe as live actors.
518
+When Alice walks into the room (itself an actor), the room gives Alice
519
+a reference to the goblin actor.
520
+To &quot;see&quot; the goblin, Alice sends a message to it asking for its
521
+description.
522
+It replies with its datashards storage URI with its 3d model and associated
523
+textures.
524
+Alice can now query the storage system to reconstruct these models and
525
+textures from the datashards storage systems she uses.
526
+(The datashards storage systems themselves can&#39;t actually see the
527
+contents if they don&#39;t have the capability itself; this is much safer
528
+for peers to help the network share data because they can help route
529
+things through the network without personally knowing or being
530
+responsible for what the contents of those messages are.
531
+It could also be possible for the goblin to provide Alice with a direct
532
+channel to a storage system to retrieve its assets from.)
533
+Horray, Alice got the 3d model and images!
534
+Now she can see the goblin.&lt;/p&gt;
535
+&lt;p&gt;Assuming that the goblin is an enemy, Alice attacks!
536
+Attacking is common in this game universe, and there is no reason
537
+necessarily to keep around attack messages, so sending a message to
538
+the goblin is just a one-off transient message... there&#39;s no need
539
+to persist it in the storage system.&lt;/p&gt;
540
+&lt;p&gt;The attack misses!
541
+The goblin shouts, &quot;Wait!&quot; and makes its case, that both of them are
542
+just adventurers in this room, and shouldn&#39;t they both be friends?
543
+Alice is touched and halts her attack.
544
+These messages are also sent transiently; while either party could
545
+log them, they are closer to an instant messenger or IRC conversation
546
+rather than something intended to be persisted long-term.&lt;/p&gt;
547
+&lt;p&gt;They exchange their mailbox addresses and begin sending each other
548
+letters.
549
+These, however, are intended to be persisted; when Alice receives a
550
+message from the goblin in her mailbox (or vice versa), the message
551
+received contains the datashards URI to the letter, which Alice can
552
+then retrieve from the appropriate store.
553
+She can then always refer to this message, and she can choose whether
554
+or not to persist it locally or elsewhere.
555
+Since the letter has its own storage URI, when Alice constructs a
556
+reply, she can clearly mark that it was in reference to the previous
557
+letter.
558
+Even if Alice or the goblin&#39;s servers go down, either can continue
559
+to refer to these letters.
560
+Alice and the goblin have the freedom to choose what storage systems
561
+they wish, whether targeted/direct/local or via a public peer to peer
562
+routing system, with reasonable assumptions (given the continued
563
+strength of the underlying cryptographic algorithms used) that the
564
+particular entities storing or forwarding their data cannot read its
565
+content.&lt;/p&gt;
566
+&lt;p&gt;And so it is: live references of actors are able to send live,
567
+transient messages, but can only be sent to other actors whose
568
+(unguessable/unforgeable) address you have.
569
+This allows for highly dynamic and expressive interactions while
570
+retaining security.
571
+Datashards URIs allow for the storage and retrieval of content which
572
+can continue to be persisted by interested parties, even if the
573
+originating host goes down.&lt;/p&gt;
574
+&lt;p&gt;There are some things I glossed over in this writeup.
575
+The particular ways that the actors&#39; addresses and references work is
576
+one thing (unguessable http based capability URLs on their own
577
+have &lt;a class=&quot;reference external&quot; href=&quot;https://www.w3.org/TR/capability-urls/&quot;&gt;leakage problems&lt;/a&gt;
578
+due to the way various web technologies are implemented, and
579
+not even every actor reference needs to be a long-lived URI;
580
+see &lt;a class=&quot;reference external&quot; href=&quot;http://erights.org/elib/distrib/captp/index.html&quot;&gt;CapTP for more details&lt;/a&gt;),
581
+how to establish connections between actor processes/servers
582
+(we can reuse TLS, or even better, something like tor&#39;s onion services),
583
+so are how interactions such as fighting can be scoped to a
584
+room (&lt;a class=&quot;reference external&quot; href=&quot;https://www.uni-weimar.de/fileadmin/user/fak/medien/professuren/Virtual_Reality/documents/publications/capsec_vr2008_preprint.pdf&quot;&gt;this paper&lt;/a&gt;
585
+explains how), as well as how we can map human meaningful names
586
+onto unguessable identifiers (the answer there is
587
+&lt;a class=&quot;reference external&quot; href=&quot;https://github.com/cwebber/rebooting-the-web-of-trust-spring2018/blob/petnames/draft-documents/making-dids-invisible-with-petnames.md&quot;&gt;petnames&lt;/a&gt;).
588
+But I have plans for this and increasing confidence that it will come
589
+together... I think we&#39;re already on track.&lt;/p&gt;
590
+&lt;p&gt;Hopefully this writeup brings some clarity on how some of the
591
+components will work together, though!&lt;/p&gt;</content>
592
+	<author>
593
+	  <name>Christopher Lemmer Webber</name>
594
+	   <uri>http://dustycloud.org/</uri> 
595
+	</author>
596
+	<source>
597
+	  <title type="html">DustyCloud Brainstorms</title>
598
+	  
599
+	  <link rel="self" href="http://dustycloud.org/blog/index.xml"/>
600
+	  <id>http://dustycloud.org/</id>  
601
+	</source>
602
+  </entry>
603
+  
604
+  <entry xml:lang="en">
605
+	<title type="html" xml:lang="en">GNU Spotlight with Mike Gerwitz: 17 new GNU releases in June!</title>
606
+	<link href="http://www.fsf.org/blogs/community/gnu-spotlight-with-mike-gerwitz-17-new-gnu-releases-in-june"/>
607
+	<id>http://www.fsf.org/blogs/community/gnu-spotlight-with-mike-gerwitz-17-new-gnu-releases-in-june</id>
608
+	<updated>2019-06-27T16:08:17+00:00</updated>
609
+	<summary type="html" xml:lang="en"></summary>
610
+	<content type="html" xml:lang="en">&lt;ul&gt;
611
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/apl/&quot;&gt;apl-1.8&lt;/a&gt;&lt;/li&gt;
612
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/artanis/&quot;&gt;artanis-0.3.2&lt;/a&gt;&lt;/li&gt;
613
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/dr-geo/&quot;&gt;dr-geo-19.06a&lt;/a&gt;&lt;/li&gt;
614
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/gawk/&quot;&gt;gawk-5.0.1&lt;/a&gt;&lt;/li&gt;
615
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/gengetopt/&quot;&gt;gengetopt-2.23&lt;/a&gt;&lt;/li&gt;
616
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/gnunet/&quot;&gt;gnunet-0.11.5&lt;/a&gt;&lt;/li&gt;
617
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/guile/&quot;&gt;guile-2.2.5&lt;/a&gt;&lt;/li&gt;
618
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/gnuzilla/&quot;&gt;icecat-60.7.0-gnu1&lt;/a&gt;&lt;/li&gt;
619
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/libmicrohttpd/&quot;&gt;libmicrohttpd-0.9.64&lt;/a&gt;&lt;/li&gt;
620
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/libredwg/&quot;&gt;libredwg-0.8&lt;/a&gt;&lt;/li&gt;
621
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/mailutils/&quot;&gt;mailutils-3.7&lt;/a&gt;&lt;/li&gt;
622
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/mit-scheme/&quot;&gt;mit-scheme-10.1.9&lt;/a&gt;&lt;/li&gt;
623
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/nano/&quot;&gt;nano-4.3&lt;/a&gt;&lt;/li&gt;
624
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/nettle/&quot;&gt;nettle-3.5&lt;/a&gt;&lt;/li&gt;
625
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/parallel/&quot;&gt;parallel-20190622&lt;/a&gt;&lt;/li&gt;
626
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/unifont/&quot;&gt;unifont-12.1.02&lt;/a&gt;&lt;/li&gt;
627
+&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/units/&quot;&gt;units-2.19&lt;/a&gt;&lt;/li&gt;
628
+&lt;/ul&gt;
629
+&lt;p&gt;For announcements of most new GNU releases, subscribe to the info-gnu
630
+mailing list: &lt;a href=&quot;https://lists.gnu.org/mailman/listinfo/info-gnu&quot;&gt;https://lists.gnu.org/mailman/listinfo/info-gnu&lt;/a&gt;.&lt;/p&gt;
631
+&lt;p&gt;To download: nearly all GNU software is available from
632
+&lt;a href=&quot;https://ftp.gnu.org/gnu/&quot;&gt;https://ftp.gnu.org/gnu/&lt;/a&gt;, or preferably one of its mirrors from
633
+&lt;a href=&quot;https://www.gnu.org/prep/ftp.html&quot;&gt;https://www.gnu.org/prep/ftp.html&lt;/a&gt;. You can use the URL
634
+&lt;a href=&quot;https://ftpmirror.gnu.org/&quot;&gt;https://ftpmirror.gnu.org/&lt;/a&gt; to be automatically redirected to a
635
+(hopefully) nearby and up-to-date mirror.&lt;/p&gt;
636
+&lt;p&gt;A number of GNU packages, as well as the GNU operating system as a
637
+whole, are looking for maintainers and other assistance: please see
638
+&lt;a href=&quot;https://www.gnu.org/server/takeaction.html#unmaint&quot;&gt;https://www.gnu.org/server/takeaction.html#unmaint&lt;/a&gt; if you&#39;d like to
639
+help. The general page on how to help GNU is at
640
+&lt;a href=&quot;https://www.gnu.org/help/help.html&quot;&gt;https://www.gnu.org/help/help.html&lt;/a&gt;.&lt;/p&gt;
641
+&lt;p&gt;If you have a working or partly working program that you&#39;d like
642
+to offer to the GNU project as a GNU package, see
643
+&lt;a href=&quot;https://www.gnu.org/help/evaluation.html&quot;&gt;https://www.gnu.org/help/evaluation.html&lt;/a&gt;.&lt;/p&gt;
644
+&lt;p&gt;As always, please feel free to write to us at &lt;a href=&quot;mailto:maintainers@gnu.org&quot;&gt;maintainers@gnu.org&lt;/a&gt;
645
+with any GNUish questions or suggestions for future installments.&lt;/p&gt;</content>
646
+	<author>
647
+	  <name>FSF Blogs</name>
648
+	   <uri>http://www.fsf.org/blogs/recent-blog-posts</uri> 
649
+	</author>
650
+	<source>
651
+	  <title type="html">FSF blogs</title>
652
+	  <subtitle type="html">Writing by representatives of the Free Software Foundation.</subtitle>
653
+	  <link rel="self" href="http://static.fsf.org/fsforg/rss/blogs.xml"/>
654
+	  <id>http://www.fsf.org/blogs/recent-blog-posts</id>  
655
+	</source>
656
+  </entry>
657
+  
658
+  <entry xml:lang="en">
659
+	<title type="html" xml:lang="en">fibs, lies, and benchmarks</title>
660
+	<link href="http://wingolog.org/archives/2019/06/26/fibs-lies-and-benchmarks"/>
661
+	<id>http://wingolog.org/2019/06/26/fibs-lies-and-benchmarks</id>
662
+	<updated>2019-06-26T10:34:11+00:00</updated>
663
+	<summary type="html" xml:lang="en"></summary>
664
+	<content type="html" xml:lang="en">&lt;div&gt;&lt;p&gt;Friends, consider the recursive Fibonacci function, expressed most lovelily in Haskell:&lt;/p&gt;&lt;pre&gt;fib 0 = 0
665
+fib 1 = 1
666
+fib n = fib (n-1) + fib (n-2)
667
+&lt;/pre&gt;&lt;p&gt;Computing elements of the Fibonacci sequence (&quot;Fibonacci numbers&quot;) is a common microbenchmark.  Microbenchmarks are like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Suzuki_method&quot;&gt;Suzuki exercises for learning violin&lt;/a&gt;: not written to be good tunes (good programs), but rather to help you improve a skill.&lt;/p&gt;&lt;p&gt;The &lt;tt&gt;fib&lt;/tt&gt; microbenchmark teaches language implementors to improve recursive function call performance.&lt;/p&gt;&lt;p&gt;I&#39;m writing this article because after adding native code generation to Guile, I wanted to check how Guile was doing relative to other language implementations.  The results are mixed.  We can start with the most favorable of the comparisons: Guile present versus Guile of the past.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;center&gt;&lt;img src=&quot;http://wingolog.org/pub/guile-versions.png&quot; /&gt;&lt;br /&gt;&lt;/center&gt;&lt;p&gt;I collected these numbers on my i7-7500U CPU @ 2.70GHz 2-core laptop, with no particular performance tuning, running each benchmark 10 times, waiting 2 seconds between measurements.  The bar value indicates the median elapsed time, and above each bar is an overlayed histogram of all results for that scenario.  Note that the y axis is on a log scale.  The 2.9.3* version corresponds to unreleased Guile from git.&lt;/p&gt;&lt;p&gt;Good news: Guile has been getting significantly faster over time!  Over decades, true, but I&#39;m pleased.&lt;/p&gt;&lt;p&gt;&lt;b&gt;where are we?  static edition&lt;/b&gt;&lt;/p&gt;&lt;p&gt;How good are Guile&#39;s numbers on an absolute level?  It&#39;s hard to say because there&#39;s no absolute performance oracle out there.  However there are relative performance oracles, so we can try out perhaps some other language implementations.&lt;/p&gt;&lt;p&gt;First up would be the industrial C compilers, GCC and LLVM.  We can throw in a few more &quot;static&quot; language implementations as well: compilers that completely translate to machine code ahead-of-time, with no type feedback, and a minimal run-time.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;center&gt;&lt;img src=&quot;http://wingolog.org/pub/static-languages.png&quot; /&gt;&lt;br /&gt;&lt;/center&gt;&lt;p&gt;Here we see that GCC is doing best on this benchmark, completing in an impressive 0.304 seconds.  It&#39;s interesting that the result differs so much from clang.  I had a look at the disassembly for GCC and I see:&lt;/p&gt;&lt;pre&gt;fib:
668
+    push   %r12
669
+    mov    %rdi,%rax
670
+    push   %rbp
671
+    mov    %rdi,%rbp
672
+    push   %rbx
673
+    cmp    $0x1,%rdi
674
+    jle    finish
675
+    mov    %rdi,%rbx
676
+    xor    %r12d,%r12d
677
+again:
678
+    lea    -0x1(%rbx),%rdi
679
+    sub    $0x2,%rbx
680
+    callq  fib
681
+    add    %rax,%r12
682
+    cmp    $0x1,%rbx
683
+    jg     again
684
+    and    $0x1,%ebp
685
+    lea    0x0(%rbp,%r12,1),%rax
686
+finish:
687
+    pop    %rbx
688
+    pop    %rbp
689
+    pop    %r12
690
+    retq   
691
+&lt;/pre&gt;&lt;p&gt;It&#39;s not quite straightforward; what&#39;s the loop there for?  It turns out that &lt;a href=&quot;https://stackoverflow.com/a/10058823&quot;&gt;GCC inlines one of the recursive calls to &lt;tt&gt;fib&lt;/tt&gt;&lt;/a&gt;.  The microbenchmark is no longer measuring call performance, because GCC managed to reduce the number of calls.  If I had to guess, I would say this optimization doesn&#39;t have a wide applicability and is just to game benchmarks.  In that case, well played, GCC, well played.&lt;/p&gt;&lt;p&gt;LLVM&#39;s compiler (clang) looks more like what we&#39;d expect:&lt;/p&gt;&lt;pre&gt;fib:
692
+   push   %r14
693
+   push   %rbx
694
+   push   %rax
695
+   mov    %rdi,%rbx
696
+   cmp    $0x2,%rdi
697
+   jge    recurse
698
+   mov    %rbx,%rax
699
+   add    $0x8,%rsp
700
+   pop    %rbx
701
+   pop    %r14
702
+   retq   
703
+recurse:
704
+   lea    -0x1(%rbx),%rdi
705
+   &lt;b&gt;callq  fib&lt;/b&gt;
706
+   mov    %rax,%r14
707
+   add    $0xfffffffffffffffe,%rbx
708
+   mov    %rbx,%rdi
709
+   &lt;b&gt;callq  fib&lt;/b&gt;
710
+   add    %r14,%rax
711
+   add    $0x8,%rsp
712
+   pop    %rbx
713
+   pop    %r14
714
+   retq   
715
+&lt;/pre&gt;&lt;p&gt;I bolded the two recursive calls.&lt;/p&gt;&lt;p&gt;Incidentally, the &lt;tt&gt;fib&lt;/tt&gt; as implemented by GCC and LLVM isn&#39;t quite the same program as Guile&#39;s version.  If the result gets too big, GCC and LLVM will overflow, whereas in Guile we overflow into a &lt;a href=&quot;https://wingolog.org/archives/2019/05/23/bigint-shipping-in-firefox&quot;&gt;bignum&lt;/a&gt;.  Also in C, it&#39;s possible to &quot;smash the stack&quot; if you recurse too much; compilers and run-times attempt to mitigate this danger but it&#39;s not completely gone.  In Guile &lt;a href=&quot;https://wingolog.org/archives/2014/03/17/stack-overflow&quot;&gt;you can recurse however much you want&lt;/a&gt;.  Finally in Guile you can interrupt the process if you like; the compiled code is instrumented with safe-points that can be used to run profiling hooks, debugging, and so on.  Needless to say, this is not part of C&#39;s mission.&lt;/p&gt;&lt;p&gt;Some of these additional features can be implemented with no significant performance cost (e.g., via guard pages).  But it&#39;s fair to expect that they have some amount of overhead.  More on that later.&lt;/p&gt;&lt;p&gt;The other compilers are OCaml&#39;s &lt;tt&gt;ocamlopt&lt;/tt&gt;, coming in with a very respectable result; Go, also doing well; and V8 WebAssembly via Node.  As you know, you can compile C to WebAssembly, and then V8 will compile that to machine code.  In practice it&#39;s just as static as any other compiler, but the generated assembly is a bit more involved:&lt;/p&gt;&lt;pre&gt;
716
+fib_tramp:
717
+    jmp    fib
718
+
719
+fib:
720
+    push   %rbp
721
+    mov    %rsp,%rbp
722
+    pushq  $0xa
723
+    push   %rsi
724
+    sub    $0x10,%rsp
725
+    mov    %rsi,%rbx
726
+    mov    0x2f(%rbx),%rdx
727
+    mov    %rax,-0x18(%rbp)
728
+    cmp    %rsp,(%rdx)
729
+    jae    stack_check
730
+post_stack_check:
731
+    cmp    $0x2,%eax
732
+    jl     return_n
733
+    lea    -0x2(%rax),%edx
734
+    mov    %rbx,%rsi
735
+    mov    %rax,%r10
736
+    mov    %rdx,%rax
737
+    mov    %r10,%rdx
738
+    callq  fib_tramp
739
+    mov    -0x18(%rbp),%rbx
740
+    sub    $0x1,%ebx
741
+    mov    %rax,-0x20(%rbp)
742
+    mov    -0x10(%rbp),%rsi
743
+    mov    %rax,%r10
744
+    mov    %rbx,%rax
745
+    mov    %r10,%rbx
746
+    callq  fib_tramp
747
+return:
748
+    mov    -0x20(%rbp),%rbx
749
+    add    %ebx,%eax
750
+    mov    %rbp,%rsp
751
+    pop    %rbp
752
+    retq   
753
+return_n:
754
+    jmp    return
755
+stack_check:
756
+    callq  WasmStackGuard
757
+    mov    -0x10(%rbp),%rbx
758
+    mov    -0x18(%rbp),%rax
759
+    jmp    post_stack_check
760
+&lt;/pre&gt;&lt;p&gt;Apparently &lt;tt&gt;fib&lt;/tt&gt; compiles to a function of two arguments, the first passed in &lt;tt&gt;rsi&lt;/tt&gt;, and the second in &lt;tt&gt;rax&lt;/tt&gt;.  (V8 uses a custom calling convention for its compiled WebAssembly.)  The first synthesized argument is a handle onto run-time data structures for the current thread or isolate, and in the function prelude there&#39;s a check to see that the function has enough stack.  V8 uses these stack checks also to handle interrupts, for when a web page is stuck in JavaScript.&lt;/p&gt;&lt;p&gt;Otherwise, it&#39;s a more or less normal function, with a bit more register/stack traffic than would be strictly needed, but pretty good.&lt;/p&gt;&lt;p&gt;&lt;b&gt;do optimizations matter?&lt;/b&gt;&lt;/p&gt;&lt;p&gt;You&#39;ve heard of Moore&#39;s Law -- though it doesn&#39;t apply any more, it roughly translated into hardware doubling in speed every 18 months.  (Yes, I know it wasn&#39;t precisely that.)  There is a corresponding rule of thumb for compiler land, &lt;a href=&quot;http://proebsting.cs.arizona.edu/law.html&quot;&gt;Proebsting&#39;s Law&lt;/a&gt;:  compiler optimizations make software twice as fast every 18 &lt;i&gt;years&lt;/i&gt;.  Zow!&lt;/p&gt;&lt;p&gt;The previous results with GCC and LLVM were with optimizations enabled (-O3).  One way to measure Proebsting&#39;s Law would be to compare the results with -O0.  Obviously in this case the program is small and we aren&#39;t expecting much work out of the optimizer, but it&#39;s interesting to see anyway:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;center&gt;&lt;img src=&quot;http://wingolog.org/pub/optimization-levels.png&quot; /&gt;&lt;br /&gt;&lt;/center&gt;&lt;p&gt;Answer: optimizations don&#39;t matter much for this benchark.  This investigation does give a good baseline for compilers from high-level languages, like Guile: in the absence of clever trickery like the recursive inlining thing GCC does and in the absence of industrial-strength instruction selection, what&#39;s a good baseline target for a compiler?  Here we see for this benchmark that it&#39;s somewhere between 420 and 620 milliseconds or so.  Go gets there, and OCaml does even better.&lt;/p&gt;&lt;p&gt;&lt;b&gt;how is time being spent, anyway?&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Might we expect V8/WebAssembly to get there soon enough, or is the stack check that costly?  How much time does one stack check take anyway?  For that we&#39;d have to determine the number of recursive calls for a given invocation.&lt;/p&gt;&lt;p&gt;Friends, it&#39;s not entirely clear to me why this is, but I instrumented a copy of &lt;tt&gt;fib&lt;/tt&gt;, and I found that the number of calls in &lt;tt&gt;fib(&lt;i&gt;n&lt;/i&gt;)&lt;/tt&gt; was a more or less constant factor of the result of calling &lt;tt&gt;fib&lt;/tt&gt;.  That ratio converges to twice the golden ratio, which means that since &lt;tt&gt;fib(n+1) ~= φ * fib(n)&lt;/tt&gt;, then the number of calls in &lt;tt&gt;fib(n)&lt;/tt&gt; is approximately &lt;tt&gt;2 * fib(n+1)&lt;/tt&gt;.  I scratched my head for a bit as to why this is and I gave up; the Lord works in mysterious ways.&lt;/p&gt;&lt;p&gt;Anyway for &lt;tt&gt;fib(40)&lt;/tt&gt;, that means that there are around 3.31e8 calls, absent GCC shenanigans.  So that would indicate that each call for clang takes around 1.27 ns, which at turbo-boost speeds on this machine is 4.44 cycles.  At maximum throughput (4 IPC), that would indicate 17.8 instructions per call, and indeed on the &lt;tt&gt;n &amp;gt; 2&lt;/tt&gt; path I count 17 instructions.&lt;/p&gt;&lt;p&gt;For WebAssembly I calculate 2.25 nanoseconds per call, or 7.9 cycles, or 31.5 (fused) instructions at max IPC.  And indeed counting the extra jumps in the trampoline, I get 33 cycles on the recursive path.  I count 4 instructions for the stack check itself, one to save the current isolate, and two to shuffle the current isolate into place for the recursive calls.  But, compared to clang, V8 puts 6 words on the stack per call, as opposed to only 4 for LLVM.  I think with better interprocedural register allocation for the isolate (i.e.: reserve a register for it), V8 could get a nice boost for call-heavy workloads.&lt;/p&gt;&lt;p&gt;&lt;b&gt;where are we?  dynamic edition&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Guile doesn&#39;t aim to replace C; it&#39;s different.  It has garbage collection, an integrated debugger, and a compiler that&#39;s available at run-time, it is dynamically typed.  It&#39;s perhaps more fair to compare to languages that have some of these characteristics, so I ran these tests on versions of recursive &lt;tt&gt;fib&lt;/tt&gt; written in a number of languages.  Note that all of the numbers in this post include start-up time.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;center&gt;&lt;img src=&quot;http://wingolog.org/pub/dynamic-languages.png&quot; /&gt;&lt;br /&gt;&lt;/center&gt;&lt;p&gt;Here, the ocamlc line is the same as before, but using the bytecode compiler instead of the native compiler.  It&#39;s a bit of an odd thing to include but it performs so well I just had to include it.&lt;/p&gt;&lt;p&gt;I think the real takeaway here is that Chez Scheme has fantastic performance.  I have not been able to see the disassembly -- does it do the trick like GCC does? -- but the numbers are great, and I can see why Racket decided to rebase its implementation on top of it.&lt;/p&gt;&lt;p&gt;Interestingly, as far as I understand, Chez implements stack checks in the straightfoward way (an inline test-and-branch), not with a guard page, and instead of using the stack check as a generic ability to interrupt a computation in a timely manner as V8 does, Chez emits a &lt;a href=&quot;https://www.scheme.com/csug8/system.html#./system:s89&quot;&gt;separate interrupt check&lt;/a&gt;.  I would like to be able to see Chez&#39;s disassembly but haven&#39;t gotten around to figuring out how yet.&lt;/p&gt;&lt;p&gt;Since I originally published this article, I added a LuaJIT entry as well.  As you can see, LuaJIT performs as well as Chez in this benchmark.&lt;/p&gt;&lt;p&gt;Haskell&#39;s call performance is surprisingly bad here, beaten even by OCaml&#39;s bytecode compiler; is this the cost of laziness, or just a lacuna of the implementation?  I do not know.  I do know I have this mental image that Haskell is a good compiler but apparently if that&#39;s the standard, so is Guile :)&lt;/p&gt;&lt;p&gt;Finally, in this comparison section, I was not surprised by cpython&#39;s relatively poor performance; we know cpython is not fast.  I think though that it just goes to show how little these microbenchmarks are worth when it comes to user experience; like many of you I use plenty of Python programs in my daily work and don&#39;t find them slow at all.  Think of micro-benchmarks like x-ray diffraction; they can reveal the hidden substructure of DNA but they say nothing at all about the organism.&lt;/p&gt;&lt;p&gt;&lt;b&gt;where to now?&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Perhaps you noted that in the last graph, the Guile and Chez lines were labelled &quot;(lexical)&quot;.  That&#39;s because instead of running this program:&lt;/p&gt;&lt;pre&gt;(define (fib n)
761
+  (if (&amp;lt; n 2)
762
+      n
763
+      (+ (fib (- n 1)) (fib (- n 2)))))
764
+&lt;/pre&gt;&lt;p&gt;They were running this, instead:&lt;/p&gt;&lt;pre&gt;(define (fib n)
765
+  (define (fib* n)
766
+    (if (&amp;lt; n 2)
767
+        n
768
+        (+ (fib* (- n 1)) (fib* (- n 2)))))
769
+  (fib* n))
770
+&lt;/pre&gt;&lt;p&gt;The thing is, historically, Scheme programs have treated top-level definitions as being mutable.  This is because you don&#39;t know the extent of the top-level scope -- there could always be someone else who comes and adds a new definition of &lt;tt&gt;fib&lt;/tt&gt;, effectively mutating the existing definition in place.&lt;/p&gt;&lt;p&gt;This practice has its uses.  It&#39;s useful to be able to go in to a long-running system and change a definition to fix a bug or add a feature.  It&#39;s also a useful way of developing programs, to incrementally build the program bit by bit.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;center&gt;&lt;img src=&quot;http://wingolog.org/pub/top-level-vs-lexical.png&quot; /&gt;&lt;br /&gt;&lt;/center&gt;&lt;p&gt;But, I would say that as someone who as written and maintained a lot of Scheme code, it&#39;s not a normal occurence to mutate a top-level binding on purpose, and it has a significant performance impact.  If the compiler knows the target to a call, that unlocks a number of important optimizations: type check elision on the callee, more optimal closure representation, smaller stack frames, possible contification (turning calls into jumps), argument and return value count elision, representation specialization, and so on.&lt;/p&gt;&lt;p&gt;This overhead is especially egregious for calls inside modules.  Scheme-the-language only gained modules relatively recently -- relative to the history of scheme -- and one of the aspects of modules is precisely to allow reasoning about top-level module-level bindings.  This is why running Chez Scheme with the &lt;tt&gt;--program&lt;/tt&gt; option is generally faster than &lt;tt&gt;--script&lt;/tt&gt; (which I used for all of these tests): it opts in to the &quot;newer&quot; specification of what a top-level binding is.&lt;/p&gt;&lt;p&gt;In Guile we would probably like to move towards a more static way of treating top-level bindings, at least those within a single compilation unit.  But we haven&#39;t done so yet.  It&#39;s probably the most important single optimization we can make over the near term, though.&lt;/p&gt;&lt;p&gt;As an aside, it seems that LuaJIT also shows a similar performance differential for &lt;tt&gt;local function fib(n)&lt;/tt&gt; versus just plain &lt;tt&gt;function fib(n)&lt;/tt&gt;.&lt;/p&gt;&lt;p&gt;It&#39;s true though that even absent lexical optimizations, top-level calls can be made more efficient in Guile.  I am not sure if we can reach Chez with the current setup of having a &lt;a href=&quot;https://www.gnu.org/software/guile/docs/master/guile.html/Just_002dIn_002dTime-Native-Code.html#Just_002dIn_002dTime-Native-Code&quot;&gt;template JIT&lt;/a&gt;, because we need two return addresses: one virtual (for bytecode) and one &quot;native&quot; (for JIT code).  Register allocation is also something to improve but it turns out to not be so important for &lt;tt&gt;fib&lt;/tt&gt;, as there are few live values and they need to spill for the recursive call.  But, we can avoid some of the indirection on the call, probably using an inline cache associated with the callee; Chez has had this optimization since 1984!&lt;/p&gt;&lt;p&gt;&lt;b&gt;what guile learned from &lt;tt&gt;fib&lt;/tt&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;This exercise has been useful to speed up Guile&#39;s procedure calls, as you can see for the difference between the latest Guile 2.9.2 release and what hasn&#39;t been released yet (2.9.3).&lt;/p&gt;&lt;p&gt;To decide what improvements to make, I extracted the assembly that Guile generated for &lt;tt&gt;fib&lt;/tt&gt; to a standalone file, and tweaked it in a number of ways to determine what the potential impact of different scenarios was.  Some of the detritus from this investigation is &lt;a href=&quot;https://gitlab.com/wingo/fib-asm-tinkering&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;There were three big performance improvements.  One was to avoid eagerly initializing the slots in a function&#39;s stack frame; this took a surprising amount of run-time.  Fortunately the rest of the toolchain like the local variable inspector was already ready for this change.&lt;/p&gt;&lt;p&gt;Another thing that became clear from this investigation was that our stack frames were too large; there was too much memory traffic.  I was able to improve this in the lexical-call by adding an optimization to elide useless closure bindings.  Usually in Guile when you call a procedure, you pass the callee as the 0th parameter, then the arguments.  This is so the procedure has access to its closure.  For some &quot;well-known&quot; procedures -- procedures whose callers can be enumerated -- we optimize to pass a specialized representation of the closure instead (&quot;closure optimization&quot;).  But for well-known procedures with no free variables, there&#39;s no closure, so we were just passing a throwaway value (&lt;tt&gt;#f&lt;/tt&gt;).  An unhappy combination of Guile&#39;s current calling convention being stack-based and a strange outcome from the slot allocator meant that frames were a couple words too big.  Changing to allow a custom calling convention in this case sped up &lt;tt&gt;fib&lt;/tt&gt; considerably.&lt;/p&gt;&lt;p&gt;Finally, and also significantly, Guile&#39;s JIT code generation used to manually handle calls and returns via manual stack management and indirect jumps, instead of using the platform calling convention and the C stack.  This is to allow &lt;a href=&quot;https://wingolog.org/archives/2014/03/17/stack-overflow&quot;&gt;unlimited stack growth&lt;/a&gt;.  However, it turns out that the indirect jumps at return sites were stalling the pipeline.  Instead we switched to use call/return but keep our manual stack management; this allows the CPU to use its return address stack to predict return targets, speeding up code.&lt;/p&gt;&lt;p&gt;&lt;b&gt;et voilà&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Well, long article!  Thanks for reading.  There&#39;s more to do but I need to hit the publish button and pop this off my stack.  Until next time, happy hacking!&lt;/p&gt;&lt;/div&gt;</content>
771
+	<author>
772
+	  <name>Andy Wingo</name>
773
+	   <uri>http://wingolog.org/</uri> 
774
+	</author>
775
+	<source>
776
+	  <title type="html">wingolog</title>
777
+	  <subtitle type="html">A mostly dorky weblog by Andy Wingo</subtitle>
778
+	  <link rel="self" href="http://wingolog.org/feed/atom"/>
779
+	  <id>http://wingolog.org/feed/atom</id>  
780
+	</source>
781
+  </entry>
782
+  
783
+  <entry xml:lang="en">
784
+	<title type="html" xml:lang="en">GNU Emacs T-shirts available now at the GNU Press Shop</title>
785
+	<link href="http://www.fsf.org/blogs/gnu-press/emacs-t-shirts-available-now-at-the-gnu-press-shop"/>
786
+	<id>http://www.fsf.org/blogs/gnu-press/emacs-t-shirts-available-now-at-the-gnu-press-shop</id>
787
+	<updated>2019-06-25T19:20:00+00:00</updated>
788
+	<summary type="html" xml:lang="en"></summary>
789
+	<content type="html" xml:lang="en">&lt;p&gt;&lt;img alt=&quot;zoe modeling emacs tee&quot; src=&quot;https://shop.fsf.org/sites/default/files/styles/product_zoom/public/Emacs%20shirt%20three%20quarter.jpg&quot; style=&quot;margin-top: 4px;&quot; width=&quot;300&quot; /&gt; &lt;/p&gt;
790
+&lt;p&gt;Have you been waiting with bated breath for the opportunity to show your love for GNU Emacs, the text editor that also does everything else, with a nifty T-shirt? Wait no longer. The GNU Press Shop now has GNU Emacs logo T-shirts in unisex sizes S through XXXL. Order one at &lt;a href=&quot;https://shop.fsf.org/tshirts-hoodies/gnu-emacs-logo-t-shirt&quot;&gt;https://shop.fsf.org/tshirts-hoodies/gnu-emacs-logo-t-shirt&lt;/a&gt;, and we&#39;ll ship it to you sooner than you can say &quot;extensible, customizable, self-documenting, real-time display editor.&quot;&lt;/p&gt;
791
+&lt;p&gt;All GNU Press Shop purchases support the Free Software Foundation&#39;s efforts to free all software, and &lt;a href=&quot;https://my.fsf.org/join&quot;&gt;FSF associate members&lt;/a&gt; get a 20% discount off of all purchases.&lt;/p&gt;</content>
792
+	<author>
793
+	  <name>FSF Blogs</name>
794
+	   <uri>http://www.fsf.org/blogs/recent-blog-posts</uri> 
795
+	</author>
796
+	<source>
797
+	  <title type="html">FSF blogs</title>
798
+	  <subtitle type="html">Writing by representatives of the Free Software Foundation.</subtitle>
799
+	  <link rel="self" href="http://static.fsf.org/fsforg/rss/blogs.xml"/>
800
+	  <id>http://www.fsf.org/blogs/recent-blog-posts</id>  
801
+	</source>
802
+  </entry>
803
+  
804
+  <entry xml:lang="en">
805
+	<title type="html" xml:lang="en">Let&#39;s Just Be Weird Together</title>
806
+	<link href="http://dustycloud.org/blog/lets-just-be-weird-together/"/>
807
+	<id>tag:dustycloud.org,2019-06-25:blog/lets-just-be-weird-together/</id>
808
+	<updated>2019-06-25T18:10:00+00:00</updated>
809
+	<summary type="html" xml:lang="en"></summary>
810
+	<content type="html" xml:lang="en">&lt;div class=&quot;figure&quot;&gt;
811
+&lt;img alt=&quot;ascii art of weird tea mugs with steam&quot; src=&quot;http://dustycloud.org/etc/images/blog/ljbwt.gif&quot; /&gt;
812
+&lt;/div&gt;
813
+&lt;p&gt;Approximately a month ago was &lt;a class=&quot;reference external&quot; href=&quot;https://mlemmer.org/&quot;&gt;Morgan&lt;/a&gt; and I&#39;s
814
+10 year wedding anniversary.
815
+To commemorate that, and as a surprise gift, I made the above ascii
816
+art and animation.&lt;/p&gt;
817
+&lt;p&gt;Actually, it&#39;s not just an animation, it&#39;s a program, and one
818
+&lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/dustyweb/dos-hurd/blob/master/dos-hurd/examples/ljbwt.rkt&quot;&gt;you can run&lt;/a&gt;.
819
+As a side note, I originally thought I&#39;d write up how I made it,
820
+but I kept procrastinating on that and it lead me to putting off
821
+writing this post for about a month.
822
+Oh well, all I&#39;ll say for now is that it lead to a
823
+&lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely/goblinoid&quot;&gt;major rewrite&lt;/a&gt; of one of
824
+the &lt;a class=&quot;reference external&quot; href=&quot;https://gitlab.com/spritely/goblins&quot;&gt;main components of Spritely&lt;/a&gt;.
825
+But that&#39;s something to speak of for another time, I suppose.&lt;/p&gt;
826
+&lt;p&gt;Back to the imagery!
827
+Morgan was surprised to see the animation, and yet the image itself
828
+wasn&#39;t a surprise.
829
+That&#39;s because the design is actually built off of one we collaborated
830
+on together:&lt;/p&gt;
831
+&lt;div class=&quot;figure&quot;&gt;
832
+&lt;img alt=&quot;embroidery of weird tea mugs with steam&quot; src=&quot;http://dustycloud.org/etc/images/blog/ljbwt-embroidery-scaled.jpg&quot; /&gt;
833
+&lt;/div&gt;
834
+&lt;p&gt;I did the sketch for it and Morgan embroidered it.
835
+The plan is to put this above the tea station we set up in the reading
836
+area of our house.&lt;/p&gt;
837
+&lt;p&gt;The imagery and phrasing captures the philosophy of Morgan and I&#39;s
838
+relationship.
839
+We&#39;re both weird and deeply imperfect people, maybe even in some ways
840
+broken.