TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

TXR Lisp

77 pointsby chrispsnabout 5 years ago

5 comments

kazinatorabout 5 years ago
TXR is not just a pragmatic, low-dependency, small-footprint tool for hackers, but a bit of a Lisp research platform.<p>For instance, TXR Lisp doesn&#x27;t have keyword parameters &quot;natively&quot;. But via the parameter list macro mechanism[1], a function can take keyword params, if the symbol :key appears as a left item in its parameter list. That triggers the :key parameter macro.<p>TXR Lisp also has a <i>defset</i>, very similar to CL&#x27;s <i>defsetf</i>. That provides one of the ways by which we can make a form into an assignable place.<p>Now, here is the cool part. <i>defset</i>[2] and the <i>:key</i>[3] parameter macro do not know anything about each other. Yet if you write a <i>defset</i> for a form that takes key arguments, it works!<p>Quick demo:<p><pre><code> 1&gt; (defun f (:key x -- key1 key2) (list x key1 key2)) f 2&gt; (defset f (:key x -- key1 key2) val ^(f-set ,x ,key1 ,key2 ,val)) f 3&gt; (expand &#x27;(set (f 1 :key1 2 :key2 3) 42)) (f-set 1 2 3 42) </code></pre> By contract, the Common Lisp implementations of <i>defsetf</i> I&#x27;ve looked at deal with keyword parameters explicitly. They analyze the parameter list and know which symbols need gensyms and such.<p>More complex call:<p><pre><code> 4&gt; (expand &#x27;(set (f 1 :key1 (a b) :key2 (c d)) 42)) (let ((#:g0065 (a b)) (#:g0066 (c d))) (f-set 1 #:g0065 #:g0066 42)) </code></pre> <i>defset</i> ferreted out, through the <i>:key</i> expander, without knowing anything about it, that argument (a b) corresponds to the key1 parameter, and that it needs a gensym, which is then inserted in place of the ,key1 unquote in the ^(f-set ...) backquote template.<p>[1] <a href="https:&#x2F;&#x2F;www.nongnu.org&#x2F;txr&#x2F;txr-manpage.html#N-00B4065C" rel="nofollow">https:&#x2F;&#x2F;www.nongnu.org&#x2F;txr&#x2F;txr-manpage.html#N-00B4065C</a><p>[2] <a href="http:&#x2F;&#x2F;www.kylheku.com&#x2F;cgit&#x2F;txr&#x2F;tree&#x2F;share&#x2F;txr&#x2F;stdlib&#x2F;keyparams.tl" rel="nofollow">http:&#x2F;&#x2F;www.kylheku.com&#x2F;cgit&#x2F;txr&#x2F;tree&#x2F;share&#x2F;txr&#x2F;stdlib&#x2F;keypar...</a><p>[3] <a href="http:&#x2F;&#x2F;www.kylheku.com&#x2F;cgit&#x2F;txr&#x2F;tree&#x2F;share&#x2F;txr&#x2F;stdlib&#x2F;defset.tl" rel="nofollow">http:&#x2F;&#x2F;www.kylheku.com&#x2F;cgit&#x2F;txr&#x2F;tree&#x2F;share&#x2F;txr&#x2F;stdlib&#x2F;defset...</a>
chrispsnabout 5 years ago
kazinator provided examples in two recent k threads:<p><a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22631649" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22631649</a><p><a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22666539" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22666539</a>
评论 #22686062 未加载
metroholografixabout 5 years ago
I like it but I don&#x27;t know what to use it for.<p>My Lisp usage can be summarized as such:<p>For anything heavy&#x2F;performant, I use Common Lisp.<p>For everything else, I use Emacs Lisp.
kazinatorabout 5 years ago
TXR&#x27;s FFI can describe a C linked list and convert it in both directions in a call, with correct malloc&#x2F;free memory management.<p>Given this code, which is compiled as part of a larger library called &quot;crazyffi.so&quot;:<p><pre><code> struct lnode { char *datum; struct lnode *next; }; void list_update(struct lnode *list) { struct lnode *iter; int i; for (i = 0, iter = list; ; iter = iter-&gt;next, i++) { char buf[256]; printf(&quot;lnode[%d]-&gt;datum = %s\n&quot;, i, iter-&gt;datum); &#x2F;* Edit every node by adding numeric prefix to the string. * We free the old datum, and install a newly malloced * string in its place. *&#x2F; snprintf(buf, sizeof buf, &quot;%d:%s&quot;, i, iter-&gt;datum); free(iter-&gt;datum); iter-&gt;datum = strdup(buf); &#x2F;* When visiting the last node, add one more node. *&#x2F; if (!iter-&gt;next) { snprintf(buf, sizeof buf, &quot;%d:%s&quot;, i + 1, &quot;cow!&quot;); iter-&gt;next = malloc(sizeof *iter-&gt;next); iter-&gt;next-&gt;datum = strdup(buf); iter-&gt;next-&gt;next = 0; break; } } } </code></pre> This TXR Lisp code calls the function:<p><pre><code> (typedef lnode (struct lnode (datum str) (next (ptr (struct lnode))))) (with-dyn-lib &quot;.&#x2F;crazyffi.so&quot; (deffi list-update &quot;list_update&quot; void ((ptr lnode)))) (let ((ll #S(lnode datum &quot;how&quot; next #S(lnode datum &quot;now&quot; next #S(lnode datum &quot;brown&quot; next nil))))) (prinl ll) (list-update ll) (prinl ll)) </code></pre> Run it:<p><pre><code> $ txr linked-test.tl #S(lnode datum &quot;how&quot; next #S(lnode datum &quot;now&quot; next #S(lnode datum &quot;brown&quot; next nil))) lnode[0]-&gt;datum = how lnode[1]-&gt;datum = now lnode[2]-&gt;datum = brown #S(lnode datum &quot;0:how&quot; next #S(lnode datum &quot;1:now&quot; next #S(lnode datum &quot;2:brown&quot; next #S(lnode datum &quot;3:cow!&quot; next nil)))) </code></pre> We see that the list was altered with numeric prefixes on the strings, and a new node was added at the end.<p>Valgrind is completely clean:<p><pre><code> $ valgrind txr linked-test.tl ==6707== Memcheck, a memory error detector ==6707== Copyright (C) 2002-2017, and GNU GPL&#x27;d, by Julian Seward et al. [ ... ] #S(lnode datum &quot;how&quot; next #S(lnode datum &quot;now&quot; next #S(lnode datum &quot;brown&quot; next nil))) lnode[0]-&gt;datum = how lnode[1]-&gt;datum = now lnode[2]-&gt;datum = brown #S(lnode datum &quot;0:how&quot; next #S(lnode datum &quot;1:now&quot; next #S(lnode datum &quot;2:brown&quot; next #S(lnode datum &quot;3:cow!&quot; next nil)))) ==6707== ==6707== HEAP SUMMARY: ==6707== in use at exit: 2,681,402 bytes in 9,346 blocks ==6707== total heap usage: 14,827 allocs, 5,481 frees, 3,034,573 bytes allocated ==6707== ==6707== LEAK SUMMARY: ==6707== definitely lost: 0 bytes in 0 blocks ==6707== indirectly lost: 0 bytes in 0 blocks ==6707== possibly lost: 1,286,596 bytes in 1,533 blocks ==6707== still reachable: 1,394,806 bytes in 7,813 blocks ==6707== suppressed: 0 bytes in 0 blocks ==6707== Rerun with --leak-check=full to see details of leaked memory ==6707== ==6707== For counts of detected and suppressed errors, rerun with: -v ==6707== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) </code></pre> Now with --free-all:<p><pre><code> 0:sun-go:~&#x2F;txr$ valgrind txr --free-all linked-test.tl ==6721== Memcheck, a memory error detector [ ... ] #S(lnode datum &quot;how&quot; next #S(lnode datum &quot;now&quot; next #S(lnode datum &quot;brown&quot; next nil))) lnode[0]-&gt;datum = how lnode[1]-&gt;datum = now lnode[2]-&gt;datum = brown #S(lnode datum &quot;0:how&quot; next #S(lnode datum &quot;1:now&quot; next #S(lnode datum &quot;2:brown&quot; next #S(lnode datum &quot;3:cow!&quot; next nil)))) ==6721== ==6721== HEAP SUMMARY: ==6721== in use at exit: 0 bytes in 0 blocks ==6721== total heap usage: 14,830 allocs, 14,830 frees, 3,034,665 bytes allocated ==6721== ==6721== All heap blocks were freed -- no leaks are possible ==6721== ==6721== For counts of detected and suppressed errors, rerun with: -v ==6721== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) </code></pre> What&#x27;s missing is circularity detection; we cannot pass a graph back and forth in this manner. If we pass a DAG structure, its shared structure will get expanded.<p>Since circularity detection is expensive, that would have to be something enabled by some sort of attribute mechanism.
fithisuxabout 5 years ago
Interesting