-
Notifications
You must be signed in to change notification settings - Fork 17
/
curios.html
193 lines (151 loc) · 7.82 KB
/
curios.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<h1>A Quick Overview of the Curios in this Library</h1>
<h2>Clojure-Style Destructuring Bind/Function Definitions</h2>
<p>This library, <code>defn.el</code>, supports an approximation of Clojure's
destructuring bind syntax. If you are familiar with Clojure, the
following code will be recognizable:</p>
<pre><code>(require 'defn)
(defn prod
([[el & tail :as lst] acc]
(if lst (recur tail (* el acc))
acc))
([lst] (prod lst 1)))
(prod '(1 2 3 4)); -> 24
</code></pre>
<p>The above code demonstrates the declaration of a function, <code>prod</code>
which has two arities: the number of arguments determines which body
is executed. If one arg is passed in, prod calls itself with the
appropriate initial value for its accumulator. If two arguments are
provided, it uses a <code>recur</code> expression to call itself repeatedly,
calculating the produce of a list passed in. This is done without
growing the stack. You can recur within a single body perpetually. </p>
<p><code>defn</code> and its anonymous cousin <code>fn</code> support destructuring on hash
tables and association lists where each element in the list is a
proper list:</p>
<pre><code>(defn dst-demo [[:: x :x y :y]]
(list x y))
(dst-demo (tbl! :x 10 :y 11)); -> (10 11)
(dst-demo '((:x 40) (:y 50))); -> (40 50)
</code></pre>
<p>Default values may be provided for both sequential and table forms
using the <code>:or</code> keyword (clojure doesn't support <code>:or</code> for sequences).
Destructuring can be nested arbitrarily deeply as well. There are
also <code>let</code> forms which provide the same destructuring for <code>let</code>-like
situations. All these forms expand to lexical-closures, but there are
forms like <code>defn_</code> which are identical but create dynamic scopes
instead.</p>
<p>As with most heavy macro magic, you can't really use this library
without byte compiling, but when this is done, it can be reasonably
zippy. </p>
<h2>Standalone Recur</h2>
<p>I really like the <code>recur</code> feature from Clojure. I like it so much I
factored it out of the <code>defn</code> implementation so that it can be used in
situations where a lighter touch is required. </p>
<pre><code>(require 'recur)
(recur-defun* prod (lst &optional (acc 1))
(if (empty? lst)
acc
(recur (cdr lst) (* acc (car lst)))))
</code></pre>
<p>Again, this won't blow the stack. The lambda list supports the whole
<code>cl</code> style lambda list. Also provided is a <code>let</code> form which can recur
on itself, <code>recur-let</code>.</p>
<h2>Multi-methods</h2>
<p>Another Clojure-inspired feature is multi-methods. These are a kind
of proto-multiple dispatch "object" system, by which I mean they are
slightly more general than any concrete object system. You can use
them to implement a single-dispatch object system-like functionality,
for instance:</p>
<pre><code>(require 'multi-methods)
(defmulti volcalize-n-times (lambda (&rest args)
(pseudo-class-of (car args)))
"Volcalization multi-method.") ;Must be executed before defunmethod
(defunmethod volcalize-n-times :cat (thing n)
(loop for i from 1 to n do (print "Meow")))
(defunmethod volcalize-n-times :dog (thing n)
(loop for i from 1 to n do (print "Woof")))
(defunmethod volcalize-n-times :human (thing n)
(loop for i from 1 to n do (print "Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Nulla sed sapien ligula. Sed
luctus cursus consequat. Morbi egestas magna auctor dui
sagittis bibendum. Ut in felis sit amet eros eleifend ultricies
gravida id ligula. Suspendisse potenti. Nulla semper porttitor
massa, sed feugiat mauris tempor nec.")))
(defun pseudo-class-of (val)
(gethash :pseudo-class val :thing))
(volcalize-n-times (tbl! :pseudo-class :cat :name 'tibald) 10)
; prints "meow" 10 times.
(volcalize-n-times (tbl! :pseudo-class :human :name 'vincent) 2)
; prints lorem-ipsum fragment twice.
</code></pre>
<p>Multimethods match their arguments via <code>isa?</code> predicate, which can be
extended with functions like <code>derives</code>. For instance:</p>
<pre><code>($ :cat derives-from :quadraped)
($ :dog derives-from :quadraped)
($ :human derives-from :biped)
($ :emu derives-from :biped)
</code></pre>
<p>Note that <code>$</code> is a simple infix macro that swaps the function position
with the first argument position in the top level of the subsequent
forms.</p>
<pre><code>(defmulti n-legs
(lambda (&rest args)
(pseudo-class-of (car args)))
"Number of legs multi-method")
(defunmethod n-legs :biped (o) 2)
(defunmethod n-legs :quadraped (o) 4)
(n-legs (tbl! :pseudo-class :human)) ;-> 2
(n-legs (tbl! :pseudo-class :cat)) ;-> 4
</code></pre>
<p>By writing the appropriate dispatch function you can create many kinds
of crazy object systems. Dispatch is cached, so if the <code>derives</code>
hierarchy doesn't change, dispatch is fast after the first method
call.</p>
<h2>Partial Application</h2>
<p>Particular because emacs lisp is dynamically scoped, its handy to have
functions to create functions which handle lexical static for you. If
you <code>(require 'functional)</code>, you'll have access to these functions,
which make point free programming more fun:</p>
<pre><code>(mapcar (par #'+ 5) '(1 2 3));-> (6 7 8)
</code></pre>
<p><code>par</code> partially applies a function on the right. <code>pal</code> does the same
on the left. Both always return a function, so you can partially
apply more arguments than a function has, in which case when you call
the result, you'll get an error. Considering that elisp functions
have variable and unlimited arity, this seemed like the most idiomatic
accomidation. There are also some other interesting things in
<code>functional.el</code>, though it probably should be called <code>point-free.el</code>.</p>
<h2>With-stack</h2>
<p><code>with-stack.el</code> is an emacs lisp embedded stack language in the mode
of Factor or Joy. It looks like this:</p>
<pre><code>(||| 4 4 + ) ;-> 8
</code></pre>
<p>The macro <code>|||</code> introduces a stack language form. Subsequent objects
are interpreted as in a stack languages, with atomic forms pushing
themselves onto the stack and symbols acting as "words" which
manipulate the stack. The file <code>stack-words.el</code> defines many basic
stack words, but you can define your own using the <code>word:</code> word.</p>
<pre><code>(||| word: plus-four 4 + end: 10 plus-four ) ;-> 14
</code></pre>
<p><code>|||</code> returns whatever is on the top of the stack at the end of
evaluation. The stack is discarded between invocations of <code>|||</code>. The
language supports the notion of regular "words" and parsing or
immediate words, which are executed when they are encountered during
the compilation scan of the stack language segment. An immediate word
is written in the stack language itself, and sees the "future" stream
of tokens as its stack. It may manipulate that stack as it sees fit
before terminating, at which point, compilation continues till the
next immediate word or the end of the stack segment.</p>
<pre><code>(||| immediate-word: -lisp-val: '1>eval swap end:)
(||| -lisp-val: '(+ 1 1) ) ;-> 2
</code></pre>
<p>The language supports Factor/Joy style quotations and <code>stack-words</code>
defines the most common combinators (<code>if</code>,<code>loop</code>,<code>bi</code>, etc). You can
call emacs functions using a bit of sugar (shown above). The syntax
<code>2>concat</code>, for instance, means "call concat with two values from the
stack." These glue-words are assumed to return 1 value, so don't
forget to <code>drop</code> nils.</p>
<h2>Conclusion</h2>
<p>That is a bunch of the crazy stuff I have been working on. There are
other projects in a half-way state of completion. All of these
projects are on my github, as is this document. They depend on my
giant pile of utils library <code>utils.el</code>.</p>