Discussion:
on a (racket) procedure for reading an article from stdin
(too old to reply)
Julieta Shem
2023-12-26 01:24:04 UTC
Permalink
Here's a procedure for reading lines from the stdin. It implements the
handling of a POST command from NNTP. It seems to work, but it looks
like work from a beginner. I wonder if there's anything you can say to
illuminate me.

What does it do? A thread will kill the program if the user takes too
long. (Otherwise we kill the thread.) The reading ends gracefully if
the user types a ``.'' on a line by itself. Otherwise we append the
current line read onto the end of a LINES-SO-FAR list and repeat. (Yes,
if a ``..'' is on a line by itself, that's the user escaping the dot
that's not the end of input. I decided to implement this corner-case
with that /let/ before repeating.)

(define (read-with-timeout [lines-so-far '()] [timeout 60])
(define killer
(thread (lambda ()
(sleep timeout)
(displayln "Timeout on posting article.")
(flush-output)
(exit))))
(define ln (read-line (current-input-port) 'return-linefeed))
(kill-thread killer)
(cond
[(eof-object? ln)
(displayln "fatal: eof while reading article...")
(exit)]
[(string=? ln ".")
(string-join lines-so-far "\r\n")]
[else
(let ([ln (if (string=? ln "..") "." ln)])
(read-with-timeout (append lines-so-far (list ln))))]))

Any thoughts, comments? Help to be a better Lisp programmer.
Kaz Kylheku
2023-12-26 04:59:52 UTC
Permalink
Post by Julieta Shem
Here's a procedure for reading lines from the stdin. It implements the
handling of a POST command from NNTP. It seems to work, but it looks
like work from a beginner. I wonder if there's anything you can say to
illuminate me.
What does it do? A thread will kill the program if the user takes too
long. (Otherwise we kill the thread.) The reading ends gracefully if
the user types a ``.'' on a line by itself. Otherwise we append the
current line read onto the end of a LINES-SO-FAR list and repeat. (Yes,
if a ``..'' is on a line by itself, that's the user escaping the dot
that's not the end of input. I decided to implement this corner-case
with that /let/ before repeating.)
(define (read-with-timeout [lines-so-far '()] [timeout 60])
(define killer
(thread (lambda ()
(sleep timeout)
(displayln "Timeout on posting article.")
(flush-output)
(exit))))
(define ln (read-line (current-input-port) 'return-linefeed))
(kill-thread killer)
(cond
[(eof-object? ln)
(displayln "fatal: eof while reading article...")
(exit)]
[(string=? ln ".")
(string-join lines-so-far "\r\n")]
[else
(let ([ln (if (string=? ln "..") "." ln)])
(read-with-timeout (append lines-so-far (list ln))))]))
Any thoughts, comments? Help to be a better Lisp programmer.
Using a thread just for timing out a read is ugly, like something you'd
do in a shell program, not in a serious application.

(And if the shell is Bash, you wouldn't have to; it has a timed out read!
Type "help read" in bash: you can see that read can read characters
up to a specified delimiter, with a timeout (-t)).

A proper Unix program uses select or poll, or else uses setsockopt
to set up read timeout (if the input is known to be a socket).
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Julieta Shem
2023-12-26 15:25:11 UTC
Permalink
Post by Kaz Kylheku
Post by Julieta Shem
Here's a procedure for reading lines from the stdin. It implements the
handling of a POST command from NNTP. It seems to work, but it looks
like work from a beginner. I wonder if there's anything you can say to
illuminate me.
What does it do? A thread will kill the program if the user takes too
long. (Otherwise we kill the thread.) The reading ends gracefully if
the user types a ``.'' on a line by itself. Otherwise we append the
current line read onto the end of a LINES-SO-FAR list and repeat. (Yes,
if a ``..'' is on a line by itself, that's the user escaping the dot
that's not the end of input. I decided to implement this corner-case
with that /let/ before repeating.)
(define (read-with-timeout [lines-so-far '()] [timeout 60])
(define killer
(thread (lambda ()
(sleep timeout)
(displayln "Timeout on posting article.")
(flush-output)
(exit))))
(define ln (read-line (current-input-port) 'return-linefeed))
(kill-thread killer)
(cond
[(eof-object? ln)
(displayln "fatal: eof while reading article...")
(exit)]
[(string=? ln ".")
(string-join lines-so-far "\r\n")]
[else
(let ([ln (if (string=? ln "..") "." ln)])
(read-with-timeout (append lines-so-far (list ln))))]))
Any thoughts, comments? Help to be a better Lisp programmer.
Using a thread just for timing out a read is ugly, like something you'd
do in a shell program, not in a serious application.
(And if the shell is Bash, you wouldn't have to; it has a timed out read!
Type "help read" in bash: you can see that read can read characters
up to a specified delimiter, with a timeout (-t)).
A proper Unix program uses select or poll, or else uses setsockopt
to set up read timeout (if the input is known to be a socket).
In C I would have used select. But how do I select in Racket? I found
Racket's sync/timeout, a procedure based on Racket's ``events''. The
reference dissertates briefly about events at

https://docs.racket-lang.org/reference/sync.html

and they cite

John H. Reppy, Concurrent Programming in ML, Cambridge University
Press, 1999. https://doi.org/10.1017/CBO9780511574962

as a reference. It's unclear to me that this is what I'm looking for,
but it might very well be.

--8<---------------cut here---------------start------------->8---
#lang racket/base
(define (read-line-timeout [timeout 60] [port (current-input-port)])
(define port-or-false
(sync/timeout timeout port))
(and port-or-false (read-line port-or-false)))

(define (interact-with-timeout timeout)
(define ln (read-line-timeout timeout))
(cond [(not ln)
(say "> God, you're slow.")]
[(eof-object? ln)
(say "> Okay, see you later.")]
[else
(say (format "< ~a" ln))
(say "> But why do you say that?")
(interact-with-timeout timeout)]))

(define (say s) (displayln s) (flush-output))

(module+ main
(say "> Hello. Say anything and press RET.")
(interact-with-timeout 3))
--8<---------------cut here---------------end--------------->8---

I see to see a complicated behavior of this program in Windows ``Command
Prompt'', but not in the GNU EMACS eshell. On the Command Prompt,
sometimes the timeout doesn't take effect. To make it easy to hit
reproduce, I reduce the timeout to 1. Here's an example. I took way
longer than 1 second to type that ``123''. The timeout only took effect
on the second wait. Weird.

c:\something>racket timezup.rkt
Post by Kaz Kylheku
Hello. Say anything and press RET.
123
< 123
Post by Kaz Kylheku
But why do you say that?
God, you're slow.
Julieta Shem
2023-12-26 17:27:14 UTC
Permalink
Julieta Shem <***@yaxenu.org> writes:

[...]
Post by Julieta Shem
I see to see a complicated behavior of this program in Windows ``Command
Prompt'', but not in the GNU EMACS eshell. On the Command Prompt,
sometimes the timeout doesn't take effect. To make it easy to hit
reproduce, I reduce the timeout to 1. Here's an example. I took way
longer than 1 second to type that ``123''. The timeout only took effect
on the second wait. Weird.
c:\something>racket timezup.rkt
Post by Julieta Shem
Hello. Say anything and press RET.
123
< 123
Post by Julieta Shem
But why do you say that?
God, you're slow.
Here's a better presentation. I set the timeout to 1 second. After
about ten seconds, I wrote ``No timeout'' and pressed RET.

--8<---------------cut here---------------start------------->8---
c:\something>c:/sys/emacs/usr/mingw/usr/bin/time.exe racket timezup.rkt
Post by Julieta Shem
Hello. Say anything and press RET.
No timeout.
< No timeout.
Post by Julieta Shem
But why do you say that?
God, you're slow.
0.00user 0.00system 0:11.68elapsed 0%CPU (0avgtext+0avgdata 5228maxresident)k
0inputs+0outputs (1389major+0minor)pagefaults 0swaps
--8<---------------cut here---------------end--------------->8---
Julieta Shem
2023-12-29 03:21:20 UTC
Permalink
Post by Julieta Shem
[...]
Post by Julieta Shem
I see to see a complicated behavior of this program in Windows ``Command
Prompt'', but not in the GNU EMACS eshell. On the Command Prompt,
sometimes the timeout doesn't take effect. To make it easy to hit
reproduce, I reduce the timeout to 1. Here's an example. I took way
longer than 1 second to type that ``123''. The timeout only took effect
on the second wait. Weird.
c:\something>racket timezup.rkt
Post by Julieta Shem
Hello. Say anything and press RET.
123
< 123
Post by Julieta Shem
But why do you say that?
God, you're slow.
Here's a better presentation. I set the timeout to 1 second. After
about ten seconds, I wrote ``No timeout'' and pressed RET.
c:\something>c:/sys/emacs/usr/mingw/usr/bin/time.exe racket timezup.rkt
Post by Julieta Shem
Hello. Say anything and press RET.
No timeout.
< No timeout.
Post by Julieta Shem
But why do you say that?
God, you're slow.
0.00user 0.00system 0:11.68elapsed 0%CPU (0avgtext+0avgdata 5228maxresident)k
0inputs+0outputs (1389major+0minor)pagefaults 0swaps
This happens to be a known problem on Windows.

https://github.com/racket/racket/issues/4728
Lawrence D'Oliveiro
2023-12-29 02:19:44 UTC
Permalink
Post by Julieta Shem
https://docs.racket-lang.org/reference/sync.html
I had a look at that. Why do languages feel they have to stack on thick
layers of extra abstraction on top of basic POSIX concepts?

I do more Python than Lisp, and look how thin Python’s standard-library
abstractions on top of select/poll are:
<https://docs.python.org/3/library/select.html>. Makes it so much easier
to figure out what’s going on.

I suppose Racket could be worse. It could be Java.
Julieta Shem
2023-12-29 03:09:19 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Julieta Shem
https://docs.racket-lang.org/reference/sync.html
I had a look at that. Why do languages feel they have to stack on thick
layers of extra abstraction on top of basic POSIX concepts?
That seems to be the wrong thing to do for people who know the POSIX
concepts. I believe the Racket people want to create their own world
from scratch.
Post by Lawrence D'Oliveiro
I do more Python than Lisp, and look how thin Python’s standard-library
<https://docs.python.org/3/library/select.html>. Makes it so much easier
to figure out what’s going on.
True.
Post by Lawrence D'Oliveiro
I suppose Racket could be worse. It could be Java.
Yes! Way worse.
George Neuner
2023-12-29 05:50:32 UTC
Permalink
On Fri, 29 Dec 2023 02:19:44 -0000 (UTC), Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Julieta Shem
https://docs.racket-lang.org/reference/sync.html
I had a look at that. Why do languages feel they have to stack on thick
layers of extra abstraction on top of basic POSIX concepts?
For one thing, because Windows is not POSIX compliant. Languages that
try to be cross-platform have to do *something* to hide the
differences.

Whether you like the choices made by the implementors is a different
matter. ;-)
Post by Lawrence D'Oliveiro
I do more Python than Lisp, and look how thin Python’s standard-library
<https://docs.python.org/3/library/select.html>. Makes it so much easier
to figure out what’s going on.
Agreed, Python does a better job of looking like POSIX. However, in
1989 Guido had the benefit of (at least) POSIX.1 when he began to
develop Python. And as "dictator for life", it was easy to add things
when he thought they were needed.

Although the Racket project technically began in 2015, it grew out of
"PLT Scheme" which began in 1994. Scheme itself goes back to 1973 -
far before POSIX to a time when things like GUIs, modular programming,
networking, asynchronous I/O, threading, etc. were much less
important.

As a Scheme, PLT was hampered by compatibility concerns with a
language from the 1970s.


That's part of the reason the PLT group began Racket. Racket actually
is not a Scheme but rather is a unique language that syntactically
resembles Scheme and has similar (but greater) functionality.

Racket, in fact, supports readers (as in Lisp) with more or less
arbitrary parsing ability. If you are ambitous, you can create your
own DSL language having any syntax you like ... as long as it can
compile to Racket, it can be intermixed with Racket or with code from
any of a number of other DSLs that are available.

The Racket group currently is developing a new surface syntax -
tentatively called "Rhombus" - which will be more conventional infix
syntax. However, it still will be Racket underneath, so Racket and
Rhombus code will be able to be intermixed.
Post by Lawrence D'Oliveiro
I suppose Racket could be worse. It could be Java.
As a technical matter the JVM isn't bad ... it's only the Java
language that sucks. But there are compilers for many languages that
target the JVM, and most can interface to Java code if necessary. You
can code for JVM in Lisp or Scheme if you want to.


But Racket handles cross-platform quite nicely. Programs have to be
recompiled with the native compiler of course, but as long as they
don't use any non-Racket C libraries, they will be 100% *source*
portable and almost everything will just work.
[Windows and Linux return different native error codes, and of course
there is the CR/LF problem, but Racket can identify the host system
and it is pretty easy to write portable code - including GUI code -
that will run on either system.]

Current versions of Racket actually are built on Chez Scheme, and
there is a (somewhat customized) Chez runtime underneath. Using
Racket's foreign function interface (FFI) you can drop into Chez, or
into C, or into any language that supports being called from C.

With some restrictions, Racket itself supports being called from C, so
it is possible for a C program to embed a Racket instance to use
Racket libraries or even run a whole Racket program (in parallel if
the Racket instance is executed in a C thread).
Lawrence D'Oliveiro
2023-12-29 06:04:52 UTC
Permalink
... Windows is not POSIX compliant.
Wasn’t NT supposed to be POSIX-compliant in the beginning? Seems like that
whole part of the system has bitrotted away ...

So you have to ask yourself: is your return from supporting Windows users
worth the greatly increased expense?
George Neuner
2023-12-29 15:54:14 UTC
Permalink
On Fri, 29 Dec 2023 06:04:52 -0000 (UTC), Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
... Windows is not POSIX compliant.
Wasn’t NT supposed to be POSIX-compliant in the beginning? Seems like that
whole part of the system has bitrotted away ...
Not exactly. NT's "POSIX support" was to add limited signal
capability. Adopting the POSIX function APIs was never even
considered.
Post by Lawrence D'Oliveiro
So you have to ask yourself: is your return from supporting Windows users
worth the greatly increased expense?
More people directly use Windows than directly use Unix/Linux.
Lawrence D'Oliveiro
2023-12-29 20:53:25 UTC
Permalink
Post by George Neuner
On Fri, 29 Dec 2023 06:04:52 -0000 (UTC), Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
So you have to ask yourself: is your return from supporting Windows
users worth the greatly increased expense?
More people directly use Windows than directly use Unix/Linux.
If you are publishing an open-source project, then it’s not the number of
users that matters, but the level of contributions you get back from an
active community.

Bringing out a Windows build will almost certainly lead to a massive
increase in your user base and in numbers of bug reports and complaints
and general noise, without a concomitant increase in the level of
contributions back from those Windows users. Comparatively very few
Windows users have any idea about software development (or how to write
good bug reports), and the platform itself is inherently not very friendly
to open-source development.
Julieta Shem
2023-12-29 16:52:41 UTC
Permalink
Post by George Neuner
On Fri, 29 Dec 2023 02:19:44 -0000 (UTC), Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Julieta Shem
https://docs.racket-lang.org/reference/sync.html
I had a look at that. Why do languages feel they have to stack on thick
layers of extra abstraction on top of basic POSIX concepts?
For one thing, because Windows is not POSIX compliant.
Microsoft lawyers called me to say Microsoft supports ISO-IEC
9945-1:1990 --- and, having installed Cygwin, they could not distinguish
a POSIX compliant system from Microsoft Windows. I said our team would
get back to them.
George Neuner
2023-12-31 06:31:34 UTC
Permalink
Post by Julieta Shem
Post by George Neuner
On Fri, 29 Dec 2023 02:19:44 -0000 (UTC), Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Julieta Shem
https://docs.racket-lang.org/reference/sync.html
I had a look at that. Why do languages feel they have to stack on thick
layers of extra abstraction on top of basic POSIX concepts?
For one thing, because Windows is not POSIX compliant.
Microsoft lawyers called me to say Microsoft supports ISO-IEC
9945-1:1990 --- and, having installed Cygwin, they could not distinguish
a POSIX compliant system from Microsoft Windows. I said our team would
get back to them.
The key part there is "after installing cygwin".

The Windows APIs are not POSIX, but for the most part they provide
equivalent functionality, so POSIX calls can be translated into
Windows call (with varying degrees of difficulty, the asynch stuff in
particular is completely different). This is what cygwin does.

cygwin, however, has its own problems ... it is known to be unstable.


Moreover, Windows supports only a handful of signals, so if you want
to use one it doesn't support, your're out of luck.
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=msvc-170
https://www.man7.org/linux/man-pages/man7/signal.7.html
Lawrence D'Oliveiro
2023-12-31 07:08:15 UTC
Permalink
Post by George Neuner
The Windows APIs are not POSIX, but for the most part they provide
equivalent functionality, so POSIX calls can be translated into Windows
call (with varying degrees of difficulty, the asynch stuff in particular
is completely different). This is what cygwin does.
Actually, Cygwin actually manages to do something a bit better than that.

Consider the select/poll calls
<https://docs.python.org/3/library/select.html>: note the caveat that, on
Windows, they only work on sockets, whereas on regular POSIX OSes they
work on other things, in particular pipes.

Cygwin manages to implement its select/poll calls on pipes, on Windows.
Kaz Kylheku
2023-12-31 07:27:56 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by George Neuner
The Windows APIs are not POSIX, but for the most part they provide
equivalent functionality, so POSIX calls can be translated into Windows
call (with varying degrees of difficulty, the asynch stuff in particular
is completely different). This is what cygwin does.
Actually, Cygwin actually manages to do something a bit better than that.
Consider the select/poll calls
<https://docs.python.org/3/library/select.html>: note the caveat that, on
Windows, they only work on sockets, whereas on regular POSIX OSes they
work on other things, in particular pipes.
Cygwin manages to implement its select/poll calls on pipes, on Windows.
In 2016 I started a small project called Cygnal which turns the Cygwin
DLL into a "Native Windows" run-time library.

Since then, I've been using that as the basis of the Windows port of
the TXR language: that version that uses the executable installer.

You can compile a program under Cygwin, as a Cygwin program. Then
bundle it with the CYGWIN1.DLL from Cygnal, and ship it as a Cygnal
program.

As a Cygnal program, it enjoys a number of native-like conventions:

- native paths like C:\Users\bob\file.txt (no /cygdrive stuff).
- the otherwise POSIX chdir() function implements the DOS/Windows
"logged drive" concept.
- PATH is semicolon separated, once again.

- ... other things, all documented:

http://kylheku.com/cygnal

Cygnal is probably the best way to port a POSIX program to Windows.

(If there were a better way, I'd have used that instead of bothering
with making a Cygwin DLL fork.)
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Lawrence D'Oliveiro
2023-12-31 08:37:25 UTC
Permalink
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
Kaz Kylheku
2023-12-31 17:20:16 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
I don't think so. With Cygnal, you deliver an .exe accompanied by
a DLL or two, and that's it.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Lawrence D'Oliveiro
2023-12-31 21:20:47 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
I don't think so. With Cygnal, you deliver an .exe accompanied by a DLL
or two, and that's it.
WSL2 at least lets you run a proper Linux distro, with proper package
management.
De ongekruisigde
2023-12-31 21:30:18 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
I don't think so. With Cygnal, you deliver an .exe accompanied by a DLL
or two, and that's it.
WSL2 at least lets you run a proper Linux distro, with proper package
management.
Frickin Windows as a host for Linux? That's a recipe for disaster
(MS Windows must die; thank you).
Lawrence D'Oliveiro
2023-12-31 23:49:18 UTC
Permalink
Frickin Windows as a host for Linux? That's a recipe for disaster (MS
Windows must die; thank you).
Microsoft has no choice: it has to do something to stem the tide of
developers deserting Windows for Linux. Of course WSL2 is a kludge. But
then the whole history of Windows is of piling kludges on kludges, for
short-term profit-driven reasons.
Julieta Shem
2024-01-01 00:47:16 UTC
Permalink
Post by Lawrence D'Oliveiro
Frickin Windows as a host for Linux? That's a recipe for disaster (MS
Windows must die; thank you).
Microsoft has no choice: it has to do something to stem the tide of
developers deserting Windows for Linux. Of course WSL2 is a kludge. But
then the whole history of Windows is of piling kludges on kludges, for
short-term profit-driven reasons.
It's pretty useful.
Julieta Shem
2024-01-01 00:48:06 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
I don't think so. With Cygnal, you deliver an .exe accompanied by a DLL
or two, and that's it.
WSL2 at least lets you run a proper Linux distro, with proper package
management.
Funny you say proper package management as if UNIX ever had this ---
before GNU Guix.
Lawrence D'Oliveiro
2024-01-01 02:37:07 UTC
Permalink
Post by Julieta Shem
Funny you say proper package management as if UNIX ever had this ---
before GNU Guix.
Where was there a “Unix” that had this? It was pretty much developed and
refined by Debian and Red Hat, from about 1993 onwards.
Julieta Shem
2024-01-01 03:02:57 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Julieta Shem
Funny you say proper package management as if UNIX ever had this ---
before GNU Guix.
Where was there a “Unix” that had this? It was pretty much developed and
refined by Debian and Red Hat, from about 1993 onwards.
Funny you think that Debian and Red Hat ever had it. What they seem to
have done --- with all due respect --- is to compress files with
metadata. Pretty useful, but I think that /management/ should be
applied to GNU Guix, with due credit to Nix, the pioneer.
Lawrence D'Oliveiro
2024-01-01 03:07:10 UTC
Permalink
Post by Julieta Shem
Post by Lawrence D'Oliveiro
Post by Julieta Shem
Funny you say proper package management as if UNIX ever had this ---
before GNU Guix.
Where was there a “Unix” that had this? It was pretty much developed
and refined by Debian and Red Hat, from about 1993 onwards.
Funny you think that Debian and Red Hat ever had it. What they seem to
have done --- with all due respect --- is to compress files with
metadata.
They do dependency management, which is a crucial capability. It allows
for code reuse, sharing one copy of a dependency among multiple client
packages, and one-stop upgradeability for everything installed.

No offence if you didn’t understand that. Seems most Windows users have
difficulty appreciating it.
Axel Reichert
2024-01-06 18:55:00 UTC
Permalink
Post by Julieta Shem
Funny you think that Debian and Red Hat ever had it. What they seem to
have done --- with all due respect --- is to compress files with
metadata. Pretty useful, but I think that /management/ should be
applied to GNU Guix, with due credit to Nix, the pioneer.
If you are into GNU Guix (I am currently being tempted to switch), then
you are aware of Guile (methinks you mentioned it in another thread),
which brings me back to your original question: Since Guile is
advertised with its POSIX capabilities, I expect you could find
a better/more familiar technique there.

But that is just guessing and gut feeling, not based on any research.

Best regards

Axel
Julieta Shem
2024-01-06 21:32:26 UTC
Permalink
Post by Axel Reichert
Post by Julieta Shem
Funny you think that Debian and Red Hat ever had it. What they seem to
have done --- with all due respect --- is to compress files with
metadata. Pretty useful, but I think that /management/ should be
applied to GNU Guix, with due credit to Nix, the pioneer.
If you are into GNU Guix (I am currently being tempted to switch), then
you are aware of Guile (methinks you mentioned it in another thread),
I did.
Post by Axel Reichert
which brings me back to your original question: Since Guile is
advertised with its POSIX capabilities, I expect you could find a
better/more familiar technique there.
But that is just guessing and gut feeling, not based on any research.
Relative to my desires that's good intuition. I've been looking for a
Lisp as a medium of expression and Racket does appear to be the most
sophisticated one. However, it's still too early for me to be really
taken by any language --- it takes so many years. People here called my
attention to Common Lisp and even though I've only effectively spent a
few hours on it I'm impressed by how much fun it has been. Somehow
Racket has not done this for me, despite a much more considerable
investment I've already made into it --- relative to Common Lisp. But,
as you hypothesized, seeing how much fun I was having just considering
Common Lisp I did think of Guile precisely because of the GNU Guix
connection. (I asked Kaz Kylheku what he thought of Guile, but I think
he did not follow up or I did not see it somehow.)

Right now --- the way I feel --- is kinda of a no-brainer: Common Lisp
is fun. Why is it more fun than the all the beauty of Racket? I don't
know. I could elaborate, but a beginner's perspective doesn't sound
very fruitful really.

I half-jokingly remarked to a friend yesterday that reading Common Lisp
documentation I felt like a computing historian. The SBCL manual,
section 9.9, gives an example of a C function in K&R-style C --- pasted
below. (I was just messing around.) Nevertheless, I do enjoy the
history of computing quite a lot and it does feel good to actually use
tools older than the current ones that actually feels /quite/ superior
--- whatever that means. Even if it were inferior, it would still be
fun to use, but since it feels quite the opposite then it's surreally
interesting. (I saw a quote by Rich Hickey yesterday saying something
like --- patterns mean you've ran out of language --- or something like
that. I thought it was quite spot on.)

--8<---------------cut here---------------start------------->8---
struct c_struct *c_function (i, s, r, a)
int i;
char *s;
struct c_struct *r;
int a[10];
{
int j;
struct c_struct *r2;
printf("i = %d\n", i);
printf("s = %s\n", s);
printf("r->x = %d\n", r->x);
printf("r->s = %s\n", r->s);
for (j = 0; j < 10; j++) printf("a[%d] = %d.\n", j, a[j]);
r2 = (struct c_struct *) malloc (sizeof(struct c_struct));
r2->x = i + 5;
r2->s = "a C string";
return(r2);
};
--8<---------------cut here---------------end--------------->8---

Thanks for the Guile suggestion.
Raymond Wiker
2024-01-06 22:16:08 UTC
Permalink
Post by Julieta Shem
taken by any language --- it takes so many years. People here called my
attention to Common Lisp and even though I've only effectively spent a
few hours on it I'm impressed by how much fun it has been. Somehow
Racket has not done this for me, despite a much more considerable
investment I've already made into it --- relative to Common Lisp.
https://ashwinram.org/1986/01/28/a-short-ballad-dedicated-to-the-growth-of-programs/
Axel Reichert
2024-01-10 21:45:37 UTC
Permalink
Post by Julieta Shem
Post by Axel Reichert
which brings me back to your original question: Since Guile is
advertised with its POSIX capabilities, I expect you could find a
better/more familiar technique there.
But that is just guessing and gut feeling, not based on any research.
Relative to my desires that's good intuition. I've been looking for a
Lisp as a medium of expression and Racket does appear to be the most
sophisticated one.
Sounds familiar. Some years back I was torn between (Steel Bank) Common
Lisp, Clojure and Racket. From an aesthetic/minimalistic point of view,
I prefer Lisp-1s/Schemes, but SBCL is just an awesome interactive
experience in Emacs with SLIME and really fast. Clojure feels more
modern (but I am allergic against anything related to Java) and Racket
powerful (but somehow not pragmatic). Common Lisp seems more down to
earth, but also baroque (its age shows).
Post by Julieta Shem
seeing how much fun I was having just considering Common Lisp I did
think of Guile precisely because of the GNU Guix connection.
I somewhere read that Guile is the most Common-Lisp-like of the Scheme
implementations (if you ask for it, I will try to dig up the link). From
my above paragraph, you can imagine that this is intriguing for
me. Especially having a reasonably fast general purpose language that
purportedly integrates well with everything POSIX or C (I do not speak
C), can be used to configure (I should rather say "determine") the setup
of an operating system (Guix SD) and, with this OS, also for Emacs
(since Guix SD has Guile Emacs, which otherwise is said to be quite an
endeavour to build/install). I really like to reduce the number of tools
in my box, provided they are powerful and I am willing to learn them
deeply.
Post by Julieta Shem
reading Common Lisp documentation I felt like a computing historian.
[...]
Post by Julieta Shem
I do enjoy the history of computing quite a lot and it does feel good
to actually use tools older than the current ones that actually feels
/quite/ superior
Sure, and again, sounds familiar. Emacs is old, AWK is old, Unix is
old. Many of the good tools are old, it is called

https://en.wikipedia.org/wiki/Lindy_effect

Learn them early, try to master them until late in your life. (This
makes sense only for both powerful and versatile tools.)

Best regards

Axel
Lawrence D'Oliveiro
2024-01-10 22:08:16 UTC
Permalink
Emacs is old, AWK is old, Unix is old.
Many of the good tools are old ...
Only the ones that people still remember, because they are still in common
use.

And also because their modern incarnations are quite different from how
they were in the early days.
Axel Reichert
2024-01-10 22:17:37 UTC
Permalink
Post by Lawrence D'Oliveiro
Emacs is old, AWK is old, Unix is old.
Many of the good tools are old ...
Only the ones that people still remember, because they are still in common
use.
(remove-if-not #'time-resistant (list tool-1 tool-2 ...))
Post by Lawrence D'Oliveiro
And also because their modern incarnations are quite different from
how they were in the early days.
(mapcar #'refinement (list lindy-tool-1 lindy-tool-2 ...))

(-;

Best regards

Axel
Stefan Ram
2024-01-11 10:12:41 UTC
Permalink
Post by Lawrence D'Oliveiro
And also because their modern incarnations are quite different from how
they were in the early days.
I'm just following my sentimentality when choosing a LISP dialect.
In other words, it has to be as similar as possible to the LISP
I used to write simple functions for symbolic differentiation
on my Pet 2001 when I was young. Something like this:

( SETQ DIFF
( LAMBDA( X )
( COND
( ( ATOMP X )
( COND
( ( = X 'X )
1 )
( T
0 )))
( T
( COND
( ( =( CAR X )'SUM )
( LIST 'SUM( DIFF( CADR X ))( DIFF( CADDR X )))))))))

.
Lawrence D'Oliveiro
2024-01-11 22:38:02 UTC
Permalink
Post by Stefan Ram
( SETQ DIFF
( LAMBDA( X )
...
( LIST 'SUM( DIFF( CADR X ))( DIFF( CADDR X )))))))))
That looks like some Lisp-1 (Scheme-type) dialect.

Also, we don’t like doing all uppercase code these days.
Stefan Ram
2024-01-12 10:22:53 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Stefan Ram
( SETQ DIFF
( LAMBDA( X )
...
( LIST 'SUM( DIFF( CADR X ))( DIFF( CADDR X )))))))))
That looks like some Lisp-1 (Scheme-type) dialect.
For now, I'm using, IIRC, "NewLisp" and

( setq SETQ setq )
( SETQ NULL null? )
( SETQ ATOMP atom? )
( SETQ T true )
( SETQ CAR first )
( SETQ CDR rest )
( SETQ PROGN begin )
( SETQ LENGTH length )
( SETQ COND cond )
( SETQ APPEND append )
( SETQ CADR( lambda( X )( CAR ( CDR X ))))
( SETQ CADDR( lambda( X )( CAR ( CDR ( CDR X )))))

. I was not able to convert "lambda" to uppercase, though.
So, I have to write:

( SETQ DIFF
( lambda( X )
( COND
...

.
Julieta Shem
2024-01-12 14:43:01 UTC
Permalink
Post by Stefan Ram
Post by Lawrence D'Oliveiro
Post by Stefan Ram
( SETQ DIFF
( LAMBDA( X )
...
( LIST 'SUM( DIFF( CADR X ))( DIFF( CADDR X )))))))))
That looks like some Lisp-1 (Scheme-type) dialect.
For now, I'm using, IIRC, "NewLisp" and
( setq SETQ setq )
( SETQ NULL null? )
( SETQ ATOMP atom? )
( SETQ T true )
( SETQ CAR first )
( SETQ CDR rest )
( SETQ PROGN begin )
( SETQ LENGTH length )
( SETQ COND cond )
( SETQ APPEND append )
( SETQ CADR( lambda( X )( CAR ( CDR X ))))
( SETQ CADDR( lambda( X )( CAR ( CDR ( CDR X )))))
. I was not able to convert "lambda" to uppercase, though.
( SETQ DIFF
( lambda( X )
( COND
...
.
Likely because lambda is syntax, not a symbol.
Stefan Ram
2024-01-12 15:19:55 UTC
Permalink
Post by Julieta Shem
Likely because lambda is syntax, not a symbol.
The Hyper Spec (indirect quotation) does call "lambda" a "symbol":

|lambda expression n. a list which can be used in place of a
|function name in certain contexts to denote a function by
|directly describing its behavior rather than indirectly by
|referring to the name of an established function; its name
|derives from the fact that its first element is the symbol
|lambda.

. Since I use "NewLisp", what should be relevant in my case should
actually be NewLisp! The NewLisp manual says indeed:

|Note that the lambda keyword is not a symbol in a list,
|but a designator of a special type of list: the lambda list.

. Insofar, you are right when you say it's not a symbol!
It's a keyword (in NewLisp).

I'd hesitate to call lambda "syntax", but I thing you might meant
to say it's /part of/ the syntax, which is what a keyword is.
Julieta Shem
2024-01-12 17:21:38 UTC
Permalink
Post by Stefan Ram
Post by Julieta Shem
Likely because lambda is syntax, not a symbol.
|lambda expression n. a list which can be used in place of a
|function name in certain contexts to denote a function by
|directly describing its behavior rather than indirectly by
|referring to the name of an established function; its name
|derives from the fact that its first element is the symbol
|lambda.
. Since I use "NewLisp", what should be relevant in my case should
|Note that the lambda keyword is not a symbol in a list,
|but a designator of a special type of list: the lambda list.
. Insofar, you are right when you say it's not a symbol!
It's a keyword (in NewLisp).
I'd hesitate to call lambda "syntax", but I thing you might meant
to say it's /part of/ the syntax, which is what a keyword is.
We'll never go wrong if we say it's just energy. :-)
George Neuner
2024-01-10 22:57:13 UTC
Permalink
On Wed, 10 Jan 2024 22:45:37 +0100, Axel Reichert
Post by Axel Reichert
Post by Julieta Shem
Post by Axel Reichert
which brings me back to your original question: Since Guile is
advertised with its POSIX capabilities, I expect you could find a
better/more familiar technique there.
But that is just guessing and gut feeling, not based on any research.
Relative to my desires that's good intuition. I've been looking for a
Lisp as a medium of expression and Racket does appear to be the most
sophisticated one.
Sounds familiar. Some years back I was torn between (Steel Bank) Common
Lisp, Clojure and Racket. From an aesthetic/minimalistic point of view,
I prefer Lisp-1s/Schemes, but SBCL is just an awesome interactive
experience in Emacs with SLIME and really fast. Clojure feels more
modern (but I am allergic against anything related to Java) and Racket
powerful (but somehow not pragmatic). Common Lisp seems more down to
earth, but also baroque (its age shows).
Post by Julieta Shem
seeing how much fun I was having just considering Common Lisp I did
think of Guile precisely because of the GNU Guix connection.
I somewhere read that Guile is the most Common-Lisp-like of the Scheme
implementations (if you ask for it, I will try to dig up the link). From
my above paragraph, you can imagine that this is intriguing for
me. Especially having a reasonably fast general purpose language that
purportedly integrates well with everything POSIX or C (I do not speak
C), can be used to configure (I should rather say "determine") the setup
of an operating system (Guix SD) and, with this OS, also for Emacs
(since Guix SD has Guile Emacs, which otherwise is said to be quite an
endeavour to build/install). I really like to reduce the number of tools
in my box, provided they are powerful and I am willing to learn them
deeply.
Post by Julieta Shem
reading Common Lisp documentation I felt like a computing historian.
[...]
Post by Julieta Shem
I do enjoy the history of computing quite a lot and it does feel good
to actually use tools older than the current ones that actually feels
/quite/ superior
Sure, and again, sounds familiar. Emacs is old, AWK is old, Unix is
old. Many of the good tools are old, it is called
https://en.wikipedia.org/wiki/Lindy_effect
Learn them early, try to master them until late in your life. (This
makes sense only for both powerful and versatile tools.)
Best regards
Axel
Question is whether you want to write GUI programs. Then Armed Bear
Common Lisp, Clojure or Racket have the edge ... Armed Bear and
Clojure can use Java-based GUI builders and libraries, and Racket has
its own builder and libraries.

Admittedly I'm not current with Lisp state of the art, but I'm not
aware of any Lisp that comes with its own GUI builder. Certainly you
can use FFI and a widget library, or create a GUI using some other
language and connect your Lisp program to it.

YMMV,
George
Lawrence D'Oliveiro
2024-01-10 23:58:20 UTC
Permalink
... I'm not aware
of any Lisp that comes with its own GUI builder. Certainly you can use
FFI and a widget library ...
Another thing is that GUI toolkits define their own event loop. You can
hook into this with callbacks, but the fun way to do things now is via
coroutines (async/await). Scheme can implement these with continuation-
passing; but this is not a feature of Common Lisp.
Axel Reichert
2024-01-11 00:23:09 UTC
Permalink
Post by George Neuner
Question is whether you want to write GUI programs. Then Armed Bear
Common Lisp, Clojure or Racket have the edge ... Armed Bear and
Clojure can use Java-based GUI builders and libraries, and Racket has
its own builder and libraries.
Good point, but I am not affected, no GUI ambitions here.

Best regards

Axel
Julieta Shem
2024-01-11 01:05:40 UTC
Permalink
Post by Axel Reichert
Post by George Neuner
Question is whether you want to write GUI programs. Then Armed Bear
Common Lisp, Clojure or Racket have the edge ... Armed Bear and
Clojure can use Java-based GUI builders and libraries, and Racket has
its own builder and libraries.
Good point, but I am not affected, no GUI ambitions here.
Nor here.
Julieta Shem
2024-01-10 23:45:32 UTC
Permalink
Post by Axel Reichert
Post by Julieta Shem
Post by Axel Reichert
which brings me back to your original question: Since Guile is
advertised with its POSIX capabilities, I expect you could find a
better/more familiar technique there.
But that is just guessing and gut feeling, not based on any research.
Relative to my desires that's good intuition. I've been looking for a
Lisp as a medium of expression and Racket does appear to be the most
sophisticated one.
Sounds familiar. Some years back I was torn between (Steel Bank) Common
Lisp, Clojure and Racket.
That's where I am right now. I'm all for studying Racket and Clojure,
but meanwhile I'm sticking to Common Lisp. I never fell in love with a
language so quickly. These new languages may be marvelous in every
single way, but like Alan Perlis would say

``I think that it’s extraordinarily important that we in computer
science keep fun in computing. [...]'' -- Alan J. Perlis (SICP, before
the table of contents.)
Post by Axel Reichert
From an aesthetic/minimalistic point of view, I prefer
Lisp-1s/Schemes, but SBCL is just an awesome interactive experience in
Emacs with SLIME and really fast.
Maybe that's it. Really fast. Maybe that's a big thing for the fun of
it. Trivial mistakes beginners make are reported with a lot of clarity
by SBCL. I think I've never seen a more clarifying compiler ever in my
life.
Post by Axel Reichert
Clojure feels more modern (but I am allergic against anything related
to Java) and Racket powerful (but somehow not pragmatic).
It's hard to pinpoint what it is with Racket. Maybe I'm not good enough
for Racket. Maybe I'm too stupid for Racket.
Post by Axel Reichert
Common Lisp seems more down to earth, but also baroque (its age
shows).
I'd be careful here. Maybe Common Lisp is still future in various
respects. For example, when it comes to fun --- it's my top first pick.
Can't say what it is, but I'm having a lot of fun with it.
Post by Axel Reichert
Post by Julieta Shem
seeing how much fun I was having just considering Common Lisp I did
think of Guile precisely because of the GNU Guix connection.
I somewhere read that Guile is the most Common-Lisp-like of the Scheme
implementations (if you ask for it, I will try to dig up the link).
Dig it up! Let's read it and discuss it. You're invited. :-)
Post by Axel Reichert
From my above paragraph, you can imagine that this is intriguing for
me. Especially having a reasonably fast general purpose language that
purportedly integrates well with everything POSIX or C (I do not speak
C),
I think you would like to speak C. It's comforting to know what if
things ever get lower level, you'd be at home. For instance, there's
this new library that nobody created the bindings in Common Lisp or
whatever; you can do it. It's a simple thing at least for an emergency.
C is /simple/ language; that's its best feature.
Post by Axel Reichert
can be used to configure (I should rather say "determine") the setup
of an operating system (Guix SD)
What's the SD in Guix SD? Software distribution?
Post by Axel Reichert
and, with this OS, also for Emacs (since Guix SD has Guile Emacs,
which otherwise is said to be quite an endeavour to build/install). I
really like to reduce the number of tools in my box, provided they are
powerful and I am willing to learn them deeply.
Same here. I like keep the box small and I do choose tools that I can
carry with me for a life time. That's another thing about Common Lisp
that feels very good --- you find papers and books that are ``old'' and
they describe things that are still exactly like they said. That's not
even true of C, say. C has been updated to do wild things, but gladly
we can still find old compilers and even ask the new ones to behave like
the old ones. Here's a Unix counterexample:

--8<---------------cut here---------------start------------->8---
errno

Kernel designers often add system-specific macros to
errno.h. Workaround: in files that include errno.h, avoid all macros
beginning with the letter E. Recent Linux distributions (recent
versions of glibc) refuse to compile programs that declare extern int
errno without including errno.h. This is a bug, as discussed
below. Correct behavior (all other C libraries: BSD, Solaris, etc.) is
to accept extern int errno whether or not errno.h is included. The
workaround for this Linux bug is to include errno.h everywhere, for
example by compiling with gcc -include /usr/include/errno.h.

Further comments on the Linux bug: When this change was introduced, it
was a clear and flagrant violation of IEEE Std 1003.1-1990 (POSIX),
which specifies the correct declaration to be extern int errno, nothing
more, nothing less. When Paul Wanish formally requested in 2001 that
IEEE issue an "interpretation" of IEEE Std 1003.1-1990 as meaning
something other than what it says, IEEE refused:

IEEE Std 1003.1-1990 specifies that extern int errno; is the correct
declaration for errno. This is not a shorthand for a different meaning.
The glibc authors' excuse for violating IEEE Std 1003.1-1990 (and for
loudly demanding changes in subsequent standards) is that threaded
programs need a variable errno address. This excuse doesn't stand up to
even the slightest examination. Yes, threaded programs need to include
errno.h, but it's trivial to keep extern int errno working for all the
non-threaded programs. The C library simply defines an int errno and has
its address as the initial value for the thread errno pointer.

Source:
https://cr.yp.to/docs/unixport.html
--8<---------------cut here---------------end--------------->8---
Post by Axel Reichert
Post by Julieta Shem
reading Common Lisp documentation I felt like a computing historian.
[...]
Post by Julieta Shem
I do enjoy the history of computing quite a lot and it does feel good
to actually use tools older than the current ones that actually feels
/quite/ superior
Sure, and again, sounds familiar. Emacs is old, AWK is old, Unix is
old. Many of the good tools are old, it is called
https://en.wikipedia.org/wiki/Lindy_effect
Learn them early, try to master them until late in your life. (This
makes sense only for both powerful and versatile tools.)
Thanks for the name of the effect.

Let's end with something that makes a lot of sense to me.

--8<---------------cut here---------------start------------->8---
``The idea that new code is better than old is patently absurd. Old
code has been used. It has been tested. Lots of bugs have been found,
and they’ve been fixed. There’s nothing wrong with it. It doesn’t
acquire bugs just by sitting around on your hard drive. Au contraire,
baby! Is software supposed to be like an old Dodge Dart, that rusts
just sitting in the garage? Is software like a teddy bear that’s kind
of gross if it’s not made out of all new material?''
-- Joel Spolsky, 2000.

Source:
https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i
--8<---------------cut here---------------end--------------->8---
Axel Reichert
2024-01-11 00:22:00 UTC
Permalink
Post by Julieta Shem
Post by Axel Reichert
I somewhere read that Guile is the most Common-Lisp-like of the
Scheme implementations (if you ask for it, I will try to dig up the
link).
Dig it up! Let's read it and discuss it. You're invited. :-)
https://ane.iki.fi/2020/10/05/between-two-lisps.html

Somewhat more biased, and, strictly speaking, off-topic here:

https://www.wingolog.org/archives/2013/01/07/an-opinionated-guide-to-scheme-implementations
Post by Julieta Shem
I think you would like to speak C.
Sure. Not only because there is an awful lot of open source software
written in it, but also due its enormous historical importance.

https://www.buildyourownlisp.com/

(Lisp in C) is on my Emacs/Org mode to-do list for quite some time ...
Post by Julieta Shem
What's the SD in Guix SD? Software distribution?
Ah, sorry, using the old name, nowadays it is just Guix System (the OS)
versus Guix, the package manager.
Post by Julieta Shem
I like keep the box small and I do choose tools that I can carry with
me for a life time. That's another thing about Common Lisp that feels
very good --- you find papers and books that are ``old'' and they
describe things that are still exactly like they said.
Yes! When I searched around about the various Schemes, I became already
slightly annoyed: Which RxRSs are supported, which SRFIs are included,
etc.
Post by Julieta Shem
It doesn’t acquire bugs just by sitting around on your hard drive.
Non-perishable ...

Ideas rock.

Best regards

Axel
Julieta Shem
2024-01-11 09:33:14 UTC
Permalink
Post by Axel Reichert
Post by Julieta Shem
Post by Axel Reichert
I somewhere read that Guile is the most Common-Lisp-like of the
Scheme implementations (if you ask for it, I will try to dig up the
link).
Dig it up! Let's read it and discuss it. You're invited. :-)
https://ane.iki.fi/2020/10/05/between-two-lisps.html
Fun to read. There isn't much I'd like to add or comment. I agreed
with everything I read.
Post by Axel Reichert
https://www.wingolog.org/archives/2013/01/07/an-opinionated-guide-to-scheme-implementations
Less fun to read.

Question. ``Use an existing implementation rather than writing your
own. Get good at using your REPL (or the IDE, in the case of Racket).''

I use ``IDE'' very loosely and it seems the author has a more precise
definition. It seems s/he says Racket has an IDE. I suppose DrRacket.
And somehow that means Racket doesn't have a REPL?
Post by Axel Reichert
Post by Julieta Shem
I think you would like to speak C.
Sure. Not only because there is an awful lot of open source software
written in it, but also due its enormous historical importance.
Yes. It feels good to be close to the operating system so that you know
exactly how things work.
Post by Axel Reichert
https://www.buildyourownlisp.com/
(Lisp in C) is on my Emacs/Org mode to-do list for quite some time ...
You'll enjoy it! This book is great fun. Each picture is a joke. I
offered the author one of my jokes. First picture in chapter 9 at

https://www.buildyourownlisp.com/chapter9_s_expressions

The idea is: ``OLD LISP: YOU HAD TO YELL AT IT TO GET THINGS DONE''.
Post by Axel Reichert
Post by Julieta Shem
What's the SD in Guix SD? Software distribution?
Ah, sorry, using the old name, nowadays it is just Guix System (the OS)
versus Guix, the package manager.
And what did it mean?
Axel Reichert
2024-01-11 18:22:05 UTC
Permalink
Post by Julieta Shem
Post by Axel Reichert
https://www.wingolog.org/archives/2013/01/07/an-opinionated-guide-to-scheme-implementations
Less fun to read.
Why so? Too much "advocacy"?
Post by Julieta Shem
Get good at using your REPL (or the IDE, in the case of Racket).''
I use ``IDE'' very loosely
Same here. Is Emacs' lisp-interaction-mode an IDE? If not, is Emacs plus
Geiser a Scheme/Guile IDE? If not, is Emacs plus SLIME an IDE? I think
the amount of "support" a tool offers for programming is more a
continuum than either a REPL or an IDE.
Post by Julieta Shem
It seems s/he says Racket has an IDE. I suppose DrRacket.
Yes, that is probably meant.
Post by Julieta Shem
And somehow that means Racket doesn't have a REPL?
No. I suppose the author sees IDEs as supersets of REPLs, so if you have
access to the former, it is to be preferred.
Post by Julieta Shem
Post by Axel Reichert
https://www.buildyourownlisp.com/
(Lisp in C) is on my Emacs/Org mode to-do list for quite some time ...
You'll enjoy it! This book is great fun. Each picture is a joke.
Sounds good, thanks for your encouragement.
Post by Julieta Shem
Post by Axel Reichert
Ah, sorry, using the old name, nowadays it is just Guix System (the OS)
versus Guix, the package manager.
And what did it mean?
"System Distribution", see

https://en.wikipedia.org/wiki/GNU_Guix_System

Best regards

Axel
George Neuner
2024-01-15 15:07:48 UTC
Permalink
Post by Julieta Shem
Post by Julieta Shem
It's hard to pinpoint what it is with Racket. Maybe I'm not good enough
for Racket. Maybe I'm too stupid for Racket.
The full #racket language is very powerful and it can be difficult to
learn. However, there are a number of dedicated "learning" languages
which limit what the programmer can do, and have better error messages
to help newbies.
--8<---------------cut here---------------start------------->8---
Everything I say below is about me. I'm not at all evaluating anyone's
work from a non-particular and non-personal perspective.
--8<---------------cut here---------------end--------------->8---
Their error messages are very clear in these teaching languages. I'm
very grateful for having read HtDP. I consider it very highly --- I
feel there is no other publication that matches it on the objective it
set for itself. It's a unique book in the whole world to the best of my
knowledge.
I think there might be a non-small gap between HtDP and the Racket
language. I think the authors might recognize this because some of them
also wrote other books on the Racket language. I would believe that
their advice for someone like me would be to, having read HtDP, follow
up with the ``Realm of Racket'', say, which I have /tried/ and confess
the gaming context was an obstacle --- it's not like I can't abstract
all that away and focus on the language only. (I despise computer
games.)
Let's take a look at Beautiful Racket, which I've also read. Marvelous
work in the sense of showing what Racket can do, but I think it fails in
teaching because when I read a programming language book I'm putting an
engineer hat and looking for intuition on how things work on the inside
so that I can manipulate those primitives and build something I can
dream up. Beautiful Racket gives you tools that hide how the language
works (in the spirit of easeing the burden on the beginner's shoulders),
so after finishing the book, you must still continue to dig to see the
deeper level. It's the opposite of a draft such as
https://felleisen.org/matthias/lp/
that I found by accident in the last days. It's been great reading for
me. Also in the last days I've been trying to read the Evaluation
Model, but it's a bit difficult for a beginner. The draft above is
helpful because it seems to show Racket works in a very simplified
manner from the ground up in a pedagogical context.
Another hypothesis I've been considering lately (to explain my failures)
is that Racket has been taking the language-orientation hard and that
doesn't help a beginner to climb up slowly (with my psychology and
background). Let me elaborate on this point. People here gave me the
try-Common-Lisp insight in the last days. I ``invented'' some
program-project to write in it and I've been writing it (very slowly)
and having a lot of fun. But why? I think it's because of the
permissiveness of Common Lisp. Here's an example. I created a macro
that avoided some redundancies but it actually needed a variable capture
to even do what I wanted. I wanted the very wrong thing. I did it, saw
it working and then found a way of not committing such ``grave'' crime.
In Racket, I think I would not even manage to pull something like that
off because the language is designed to avoid such crimes. This implies
I need proper education before I can play with it and that seems to hurt
my solo pedagogical adventure.
I believe that the more I do Common Lisp, the more I understand Racket.
By seeing the crime I was committing, I thought of Racket --- ``oh,
maybe that's why Racket macros must received syntax objects with lexical
and scope information to go along with it''. ``The compiler won't let
me mix them up unless I learn how to hack these objects myself, which
being so clueless here I wouldn't even know how to do --- bummer. I
can't continue to do what I want, so I can't continue my Racket writing
pratice. I need to continue my documentation harvesting and so on and
maybe some days later I can come back to the writing.'' Sure I can ask
for help and so on. I've been doing it. If a ladder is so steep that a
weak person can't go up, it seems to delay the moment where one finds
enough strength to go up.
I understand serious languages are designed to protect programmers from
making unwise things, but maybe that has an impact on education. Seeing
the lab explode is likely educational and things do not explode if
building the contraptions are sufficiently hard.
Keep in mind that I'm not asking the Racket language to be any easier on
me or somehow adapt to my psychology. I'm a USENET member, so I can
share my experience just for the fun of it. I should also say that
Common Lisp has allowed me in a few days to get an enthusiasm than I
haven't gotten with any Racket program I've written in the past --- I
wrote two small systems that worked in production for various years and
are now retired; both of them would periodically download information
from an API source, store it locally and serve it to other systems. I
totally consider that my Racket reading and (little) practice has surely
prepared me for having all the fun I'm having with Common Lisp in the
last days.
I never read any of those books 8-) I learned Scheme and dabbled with
Lisp long before Racket existed.


The thing to keep in mind is that Racket essentially is an enhanced
Scheme. *Most* of what you learn re: Scheme is transferable to
Racket. The major difference is that, by default, Racket has
immutable pairs (conses) and structs - so, e.g., simple demo programs
that use set-car! and set-cdr! just won't work.

Making your structs mutable is an option - the default idiom is RAII.

Also optional are immutable strings, vectors, hashtables, boxes, etc.
[some things can be done more efficiently when the compiler knows the
data won't change].

And if you really DO need mutable pairs in Racket, they do exist - but
as a separate type called 'mpair'. Or you can use #lang R5RS or R6RS
for code that needs mutable pairs. Any #lang module can export
functions to be used by any other #lang module.

[Honestly, except for a quick hack job, I have never had a use for
mutable pairs. Anything you can do with a pair is better done with a
struct or a box. That goes for Lisp as well.]


And for the rest: anything beyond what is in R_RS Scheme necessarily
is implementation specific. Racket has more to learn than some other
implementations because, out-of-the-box, it addresses many programming
tasks ... but you learn things as you need them.

Typically it takes years to become really proficient with most
languages and/or their implementations: standard libraries, runtimes,
etc. Why should Racket be any different?


YMMV,
George
Axel Reichert
2024-01-20 09:15:59 UTC
Permalink
Post by Julieta Shem
having read HtDP. I consider it very highly
Yes, a nice and well-structured book.
Post by Julieta Shem
Beautiful Racket gives you tools that hide how the language works (in
the spirit of easeing the burden on the beginner's shoulders), so
after finishing the book, you must still continue to dig to see the
deeper level.
Well, aren't Lisps supposed to be extremely high-level languages?
Post by Julieta Shem
It's the opposite of a draft such as
https://felleisen.org/matthias/lp/
that I found by accident in the last days.
Thanks for the pointer, I did not know it.
Post by Julieta Shem
I created a macro that avoided some redundancies but it actually
needed a variable capture to even do what I wanted.
Could it be that you re-invented anaphoric macros?

https://unintelligible.org/onlisp/onlisp.html#SEC99

"On Lisp" as a whole will probably suit you well.
Post by Julieta Shem
I wanted the very wrong thing. I did it, saw it working and then
found a way of not committing such ``grave'' crime.
With your C background you should know that you can do many things as
long as you know what you are doing. (-:

Best regards

Axel
Julieta Shem
2024-01-20 10:30:21 UTC
Permalink
[...]
Post by Axel Reichert
Post by Julieta Shem
Beautiful Racket gives you tools that hide how the language works (in
the spirit of easeing the burden on the beginner's shoulders), so
after finishing the book, you must still continue to dig to see the
deeper level.
Well, aren't Lisps supposed to be extremely high-level languages?
The context here is Beautiful Racket. Instead of teaching you how to
use the Racket primitives, it teaches you how to use its own primitives.
So in practice you learn the Beautiful Racket language, not Racket. It
works like a tour to see what the Racket language can do if you master
it --- likely its very objective. But then when you're done, you still
need to study the Racket language. And with a POSIX background, you'll
wonder where things are in the Racket language as well.

It's the language barrier. You may know what you want to say, but if
you're in a new world you still need to learn all the new world's ways.
Post by Axel Reichert
Post by Julieta Shem
I created a macro that avoided some redundancies but it actually
needed a variable capture to even do what I wanted.
Could it be that you re-invented anaphoric macros?
https://unintelligible.org/onlisp/onlisp.html#SEC99
Thank you for the name of it. Paolo Amoroso had already pointed out
Doug Hoyte's book. (Thanks, Paolo.)
Post by Axel Reichert
"On Lisp" as a whole will probably suit you well.
Thank you!
Axel Reichert
2024-01-20 10:35:36 UTC
Permalink
Post by Julieta Shem
Post by Axel Reichert
Could it be that you re-invented anaphoric macros?
https://unintelligible.org/onlisp/onlisp.html#SEC99
Thank you for the name of it. Paolo Amoroso had already pointed out
Doug Hoyte's book. (Thanks, Paolo.)
Post by Axel Reichert
"On Lisp" as a whole will probably suit you well.
Thank you!
... and will be far easier than "Let Over Lambda" (but still quite
advanced).

Best regards

Axel
Julieta Shem
2024-01-20 10:26:22 UTC
Permalink
On Mon, 15 Jan 2024 00:32:11 -0300
example. I created a macro that avoided some redundancies but it
actually needed a variable capture to even do what I wanted. I
wanted the very wrong thing. I did it, saw it working and then found
Variable capture in macros isn't necessarily wrong. The book "Let Over
Lambda"[1] by Doug Hoyte presents some interesting examples of
intentional variable capture in Common Lisp macros.
Thank you! I think that's chapter 6, anaphoric macros.
Paolo Amoroso
2024-01-20 16:42:20 UTC
Permalink
On Sat, 20 Jan 2024 07:26:22 -0300
Post by Julieta Shem
Variable capture in macros isn't necessarily wrong. The book "Let
Over Lambda"[1] by Doug Hoyte presents some interesting examples of
intentional variable capture in Common Lisp macros.
Thank you! I think that's chapter 6, anaphoric macros.
Yes, that's it. Paul Graham's classic "On Lisp" covers anaphoric macros
too.
Julieta Shem
2024-01-20 19:48:27 UTC
Permalink
Post by Paolo Amoroso
On Sat, 20 Jan 2024 07:26:22 -0300
Post by Julieta Shem
Variable capture in macros isn't necessarily wrong. The book "Let
Over Lambda"[1] by Doug Hoyte presents some interesting examples of
intentional variable capture in Common Lisp macros.
Thank you! I think that's chapter 6, anaphoric macros.
Yes, that's it. Paul Graham's classic "On Lisp" covers anaphoric macros
too.
Doug Hoyte mentions Paul Graham. He begins chapter 6 with

``Some of the most interesting macros from Paul Graham's On Lisp are
anaphoric macros.''

and promply discuss alambda:

--8<---------------cut here---------------start------------->8---
;; Graham's alambda
(defmacro alambda (parms &body body)
`(labels ((self ,parms ,@body))
#'self))
--8<---------------cut here---------------end--------------->8---

Paul Graham seems to credit Richard Draves for having ``rewrit[ten]''
alambda in 1985 (in ``Acknowledgements'').

--8<---------------cut here---------------start------------->8---
Several other people consented to read all or part of the manuscript,
including Tom Cheatham, Richard Draves (who also rewrote alambda and
propmacro back in 1985), John Foderaro, David Hendler, George Luger,
Robert Muller, Mark Nitzberg, and Guy Steele.
--8<---------------cut here---------------end--------------->8---
Kaz Kylheku
2024-01-21 04:48:14 UTC
Permalink
Post by Julieta Shem
Thank you! I think that's chapter 6, anaphoric macros.
Breaking news! Evidence of amphoric Lisp macros was found in an
acient Greek clay jar!
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Julieta Shem
2024-01-21 14:18:12 UTC
Permalink
Post by Kaz Kylheku
Post by Julieta Shem
Thank you! I think that's chapter 6, anaphoric macros.
Breaking news! Evidence of amphoric Lisp macros was found in an
acient Greek clay jar!
What's up? Are you saying the book is too old? Or I'm discovering a
very old thing? Beginners don't get the jokes. I also have no idea why
you misspelled ``anaphoric''---dictionary did not help.
Spiros Bousbouras
2024-01-21 15:37:00 UTC
Permalink
On Sun, 21 Jan 2024 11:18:12 -0300
Post by Julieta Shem
Post by Kaz Kylheku
Post by Julieta Shem
Thank you! I think that's chapter 6, anaphoric macros.
Breaking news! Evidence of amphoric Lisp macros was found in an
acient Greek clay jar!
What's up? Are you saying the book is too old? Or I'm discovering a
very old thing? Beginners don't get the jokes. I also have no idea why
you misspelled ``anaphoric''---dictionary did not help.
I think it was just a silly pun/joke. Google knows about "amphoric" but I
think that "amphora" was intended for the pun.

Kaz Kylheku
2024-01-01 03:20:05 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Cygnal is probably the best way to port a POSIX program to Windows.
There is a better way now, and that is WSL2.
I don't think so. With Cygnal, you deliver an .exe accompanied by a DLL
or two, and that's it.
WSL2 at least lets you run a proper Linux distro, with proper package
management.
That's nice, but not what you want when you want to ship a Windows
application: .exe plus a few .dll files, which run as ordinary Windows
applications without a special additional environment.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Lawrence D'Oliveiro
2024-01-01 04:46:31 UTC
Permalink
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
WSL2 at least lets you run a proper Linux distro, with proper package
management.
That's nice, but not what you want when you want to ship a Windows
application ...
Which is why WSL2 is likely to become a mandatory part of future Windows
installs. I’m not saying that was Microsoft’s conscious plan in
introducing it; but it is simply going to be the path of least resistance
going forward.
Kaz Kylheku
2024-01-01 08:07:50 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
WSL2 at least lets you run a proper Linux distro, with proper package
management.
That's nice, but not what you want when you want to ship a Windows
application ...
Which is why WSL2 is likely to become a mandatory part of future Windows
installs.
Might work in the future, and definitely won't work on older Windows,
like 7.

How easily do WSL2 programs work with the native Windows environment?

Suppose that the current directory on the F: drive is \data,
and the WSL2 program is given the path F:file.txt. Will it understand
that the path means F:\data\file.txt?

The Cygnal program will do that.

WSL's FAQ says this:

You can also access your local machine’s file system from within the
Linux Bash shell – you’ll find your local drives mounted under the
/mnt folder. For example, your C: drive is mounted under /mnt/c:

That's not native; it's like /cygdrive/c in Cygwin. I fixed that;
I got rid of /cygdrive/c and most of the path munging.

WSL2 does not look like a way, in any shape or form, of porting a
POSIX program to be a *native* Windows application.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Kaz Kylheku
2024-01-01 09:20:38 UTC
Permalink
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
WSL2 at least lets you run a proper Linux distro, with proper package
management.
That's nice, but not what you want when you want to ship a Windows
application ...
Which is why WSL2 is likely to become a mandatory part of future Windows
installs.
Might work in the future, and definitely won't work on older Windows,
like 7.
How easily do WSL2 programs work with the native Windows environment?
Suppose that the current directory on the F: drive is \data,
and the WSL2 program is given the path F:file.txt. Will it understand
that the path means F:\data\file.txt?
The Cygnal program will do that.
You can also access your local machine’s file system from within the
Linux Bash shell – you’ll find your local drives mounted under the
That's not native; it's like /cygdrive/c in Cygwin. I fixed that;
I got rid of /cygdrive/c and most of the path munging.
In a Cygnal program you can use Win32 APIs. Can you use Win32 APIs
in a WSL2 program?

In the same C program, you can do this

#include <termios.h>

and use tcgetattr/tcsetattr to fiddle with TTY settings that
really work with Windows consoles, and you can do this:

#include <windows.h>

and use MessageBox, CreateWindow and whatnot.

Your mostly POSIX program can have some Windows-specific personality
bits to it.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Lawrence D'Oliveiro
2024-01-01 21:34:27 UTC
Permalink
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
Which is why WSL2 is likely to become a mandatory part of future
Windows installs.
Might work in the future, and definitely won't work on older Windows,
like 7.
Which nobody uses any more--nobody that Microsoft cares about, anyway.
Post by Kaz Kylheku
How easily do WSL2 programs work with the native Windows environment?
They don’t need to, won’t need to.

Windows programs, on the other hand, will be working, unbeknownst to them,
through an emulation layer not entirely unlike WINE, onto the Linux
kernel.
Post by Kaz Kylheku
Suppose that the current directory on the F: drive is \data,
and the WSL2 program is given the path F:file.txt. Will it understand
that the path means F:\data\file.txt?
No, it means what you said, which is “F:file.txt”. Just tried this:

***@theon:~> touch F:file.txt
***@theon:~> ls -l F:file.txt
-rw-r--r-- 1 ldo users 0 Jan 2 10:32 F:file.txt
Kaz Kylheku
2024-01-01 22:51:59 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
Which is why WSL2 is likely to become a mandatory part of future
Windows installs.
Might work in the future, and definitely won't work on older Windows,
like 7.
Which nobody uses any more--nobody that Microsoft cares about, anyway.
Post by Kaz Kylheku
How easily do WSL2 programs work with the native Windows environment?
They don’t need to, won’t need to.
It's nice that you can drop that requirement. I have that requirement.

I use the Cygnal run-time to support a programming language in the Lisp
family, and that programming language has access to a POSIX view of the
world, but also supports Windows programming via FFI.

This is achieved via simple mechanism: a run-time support C library DLL
that has the POSIX stuff earnestly implemented on top of Win32.

(It's analogous to Visual Studio's redistributable run-time library
or the newer Universal CRT. Those do not have POSIX support in them,
(or not any that is worth a damn) and also don't have a Unix-like build
environment that supports a configure shell script and all that.)
Post by Lawrence D'Oliveiro
Windows programs, on the other hand, will be working, unbeknownst to them,
through an emulation layer not entirely unlike WINE, onto the Linux
kernel.
That's just building conjectures upon guesses.
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Suppose that the current directory on the F: drive is \data,
and the WSL2 program is given the path F:file.txt. Will it understand
that the path means F:\data\file.txt?
-rw-r--r-- 1 ldo users 0 Jan 2 10:32 F:file.txt
Well, that is not native Windows behavior, is it! In Windows, F: is a
drive letter name. If it's not followed by a slash or backslash, the
rest of it is a relative path resolved relative to the current working
directory for that drive. Each drive has its own current working
directory! There is also a current drive, the "currently logged drive".
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Lawrence D'Oliveiro
2024-01-01 23:08:02 UTC
Permalink
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
How easily do WSL2 programs work with the native Windows environment?
They don’t need to, won’t need to.
It's nice that you can drop that requirement. I have that requirement.
You have the requirement to run Windows software, you only care that it
works, not that it is running on a “true” Windows kernel rather than on
Linux via emulation.
Post by Kaz Kylheku
Post by Lawrence D'Oliveiro
Post by Kaz Kylheku
Suppose that the current directory on the F: drive is \data,
and the WSL2 program is given the path F:file.txt. Will it understand
that the path means F:\data\file.txt?
-rw-r--r-- 1 ldo users 0 Jan 2 10:32 F:file.txt
Well, that is not native Windows behavior ...
Of course not. It’s Linux behaviour. That’s how programs running under
Linux expect things to work. Or at least native Linux behaviour: I’m sure
an emulation layer like WINE can make things look more Windows-like, if
you want. But then, it will no longer be a “WSL2 program”, will it? It
will just be a “Windows program”.
Julieta Shem
2024-01-05 02:04:49 UTC
Permalink
Julieta Shem <***@yaxenu.org> writes:

[...]
Post by Julieta Shem
In C I would have used select. But how do I select in Racket? I found
Racket's sync/timeout, a procedure based on Racket's ``events''. The
reference dissertates briefly about events at
https://docs.racket-lang.org/reference/sync.html
and they cite
John H. Reppy, Concurrent Programming in ML, Cambridge University
Press, 1999. https://doi.org/10.1017/CBO9780511574962
as a reference. It's unclear to me that this is what I'm looking for,
but it might very well be.
#lang racket/base
(define (read-line-timeout [timeout 60] [port (current-input-port)])
(define port-or-false
(sync/timeout timeout port))
(and port-or-false (read-line port-or-false)))
(define (interact-with-timeout timeout)
(define ln (read-line-timeout timeout))
(cond [(not ln)
(say "> God, you're slow.")]
[(eof-object? ln)
(say "> Okay, see you later.")]
[else
(say (format "< ~a" ln))
(say "> But why do you say that?")
(interact-with-timeout timeout)]))
(define (say s) (displayln s) (flush-output))
(module+ main
(say "> Hello. Say anything and press RET.")
(interact-with-timeout 3))
As an exercise for in CL, I tried to write such procedure in CL using
select(2) or poll(2). I failed: did not find documentation to use the
SB-UNIX package. I see all procedures I want are there, but I have no
instruction as how to use data construtors to prepare arguments for a
procedure such as UNIX-FAST-SELECT. I found

https://koji-kojiro.github.io/sb-docs/build/html/index.html

which seems helpful, but with no examples. Given that I could write
such program in C using select(2) or poll(2), would there be anything
you could say here that would give me a direction? Thank you! Any
interesting publication that I so far failed to have found? (For
instance, I have Peter Seibel's Practical Common Lisp, but it doesn't
seem to help me with this exercise.)
Kaz Kylheku
2024-01-05 02:55:05 UTC
Permalink
Post by Julieta Shem
which seems helpful, but with no examples. Given that I could write
such program in C using select(2) or poll(2), would there be anything
you could say here that would give me a direction? Thank you! Any
interesting publication that I so far failed to have found? (For
instance, I have Peter Seibel's Practical Common Lisp, but it doesn't
seem to help me with this exercise.)
One thing to watch out for, in any language, is if you're reading
from high level buffered streams but trying to use select or poll.
(This goes for C stdio also.)

You don't want to be accidentally blocking in select or poll
while the stream contains unread, buffered data (already transferred
from the operating system descriptor to user space).

That can happen if the stream reads more data than your program
requested. E.g. you're looking for one byte, and poll. The stream
initially has nothing so this is right. Poll unblocks when data
becomes available, so you read the one byte from the stream. But suppose
that the stream actually reads 500 bytes from the OS, giving you 1 and
buffering 499. After that, you have to read the available 499 bytes
from the stream before calling poll (unless poll is aware of streams,
rather than just a thin wrapper for the POSIX function.)

You have to put the underlying descriptor into non-blocking mode
(even if you're only reading from that one). Then whenever it polls
positive, read all available data from the stream until it hits an
error from the file descriptor with EWOULDBLOCK.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Julieta Shem
2024-01-05 04:19:21 UTC
Permalink
Post by Kaz Kylheku
Post by Julieta Shem
which seems helpful, but with no examples. Given that I could write
such program in C using select(2) or poll(2), would there be anything
you could say here that would give me a direction? Thank you! Any
interesting publication that I so far failed to have found? (For
instance, I have Peter Seibel's Practical Common Lisp, but it doesn't
seem to help me with this exercise.)
One thing to watch out for, in any language, is if you're reading
from high level buffered streams but trying to use select or poll.
(This goes for C stdio also.)
You don't want to be accidentally blocking in select or poll
while the stream contains unread, buffered data (already transferred
from the operating system descriptor to user space).
That can happen if the stream reads more data than your program
requested. E.g. you're looking for one byte, and poll. The stream
initially has nothing so this is right. Poll unblocks when data
becomes available, so you read the one byte from the stream. But suppose
that the stream actually reads 500 bytes from the OS, giving you 1 and
buffering 499. After that, you have to read the available 499 bytes
from the stream before calling poll (unless poll is aware of streams,
rather than just a thin wrapper for the POSIX function.)
You have to put the underlying descriptor into non-blocking mode
(even if you're only reading from that one). Then whenever it polls
positive, read all available data from the stream until it hits an
error from the file descriptor with EWOULDBLOCK.
I need to learn to put file descriptors in non-blocking mode. Thanks
for reminding me. Surely an abstraction to handle this is desired. The
problem seems to boil down to reading a partial line in the buffer
without having the rest of it --- say coming from the network still.
The procedure for reading such lines should somehow hold the partial
line until it completes.

I managed to discover UNIX-SIMPLE-POLL in SB-UNIX. Here's a procedure
that suffers from the problem you warned me above.

--8<---------------cut here---------------start------------->8---
(defun read-line-timeout (s)
"reads a line from stdin or times out in S seconds."
(let ((ready (sb-unix:unix-simple-poll 0 :input (* 1000 s))))
(cond (ready (format t "I read: ~a~%" (read-line)))
(t (format t "God, you're slow.~%")))))

;; main =
(read-line-timeout 3)
--8<---------------cut here---------------end--------------->8---
Madhu
2024-01-05 12:04:20 UTC
Permalink
Post by Julieta Shem
I need to learn to put file descriptors in non-blocking mode. Thanks
for reminding me. Surely an abstraction to handle this is desired. The
problem seems to boil down to reading a partial line in the buffer
without having the rest of it --- say coming from the network still.
The procedure for reading such lines should somehow hold the partial
line until it completes.
I managed to discover UNIX-SIMPLE-POLL in SB-UNIX. Here's a procedure
that suffers from the problem you warned me above.
(defun read-line-timeout (s)
"reads a line from stdin or times out in S seconds."
(let ((ready (sb-unix:unix-simple-poll 0 :input (* 1000 s))))
(cond (ready (format t "I read: ~a~%" (read-line)))
(t (format t "God, you're slow.~%")))))
;; main =
(read-line-timeout 3)
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Julieta Shem
2024-01-05 17:32:14 UTC
Permalink
Post by Madhu
Post by Julieta Shem
I need to learn to put file descriptors in non-blocking mode. Thanks
for reminding me. Surely an abstraction to handle this is desired. The
problem seems to boil down to reading a partial line in the buffer
without having the rest of it --- say coming from the network still.
The procedure for reading such lines should somehow hold the partial
line until it completes.
I managed to discover UNIX-SIMPLE-POLL in SB-UNIX. Here's a procedure
that suffers from the problem you warned me above.
(defun read-line-timeout (s)
"reads a line from stdin or times out in S seconds."
(let ((ready (sb-unix:unix-simple-poll 0 :input (* 1000 s))))
(cond (ready (format t "I read: ~a~%" (read-line)))
(t (format t "God, you're slow.~%")))))
;; main =
(read-line-timeout 3)
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Sounds interesting. It's a stream like stdin, stdout, stderr, but it
has a timeout associate with it. Reading or writing to it times out if
nothing usual happens before. Is that the idea?

It's a bit scary that this isn't done by any library given all the years
of SBCL. What's up with that? I'm not talking about CL as a standard
--- I'm talking about the implementations. In practice we need all of
these system-specific things.

I was expecting replies in the form --- hold it, young Skywalker: here's
how we've doing this for decades. I'm a total beginner. The procedure
above is my first procedure in CL. It makes me feel that the most
useful thing I can do in CL is learn about the SBCL's FFI. That's no
complaint. I'm just trying to understand the Lisp world.
Madhu
2024-01-06 16:22:48 UTC
Permalink
Post by Julieta Shem
Post by Madhu
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Sounds interesting. It's a stream like stdin, stdout, stderr, but it
has a timeout associate with it. Reading or writing to it times out if
nothing usual happens before. Is that the idea?
It's a bit scary that this isn't done by any library given all the years
of SBCL. What's up with that? I'm not talking about CL as a standard
--- I'm talking about the implementations. In practice we need all of
these system-specific things.
I was expecting replies in the form --- hold it, young Skywalker: here's
how we've doing this for decades. I'm a total beginner. The procedure
above is my first procedure in CL. It makes me feel that the most
useful thing I can do in CL is learn about the SBCL's FFI. That's no
complaint. I'm just trying to understand the Lisp world.
I searched for a published example but I didn't find it -- I have an
example in an implementation of a "https stream" a subclass of fd-stream
(probably derived from something "emarsden" posted somewhere, but i
can't find it) which specializes the fd-stream class and adds a
connection timeout. IIRC This integrates with the SERVE-EVENT
abstraction which is a co-ooperative multiprocessing layer about select,
and the implementation is tied to SERVE-EVENT, if the fd is not usable
(data does not arrive on it in the given time), it raises a timeout
error.

On second thoughts I don't think it will solve your problem. I just
thought this was the way to approach the problem: call READ and handle a
timeout condition (from the implementation)

I think the CMUCL code base still has the code for a netnews client for
hemlock, though it doesn't use this strategy. You can see the code from
1980, (which has not yet been deleted by some open source who has been
hired to delete it) here
https://gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/hemlock/netnews.lisp
(or wihout js)
https://gitlab.common-lisp.net/cmucl/cmucl/-/raw/master/src/hemlock/netnews.lisp

I checked trivial-nntp (probably in quicklisp) but it just wraps usocket
and doesnt deal with timeouts. iolib also doesnt seem to expose any
setsock SO_RCVTIMEO, though it can probably used, i havent figured it
out, i'll have to look again later.
Julieta Shem
2024-01-06 21:43:24 UTC
Permalink
Post by Madhu
Post by Julieta Shem
Post by Madhu
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Sounds interesting. It's a stream like stdin, stdout, stderr, but it
has a timeout associate with it. Reading or writing to it times out if
nothing usual happens before. Is that the idea?
It's a bit scary that this isn't done by any library given all the years
of SBCL. What's up with that? I'm not talking about CL as a standard
--- I'm talking about the implementations. In practice we need all of
these system-specific things.
I was expecting replies in the form --- hold it, young Skywalker: here's
how we've doing this for decades. I'm a total beginner. The procedure
above is my first procedure in CL. It makes me feel that the most
useful thing I can do in CL is learn about the SBCL's FFI. That's no
complaint. I'm just trying to understand the Lisp world.
I searched for a published example but I didn't find it -- I have an
example in an implementation of a "https stream" a subclass of fd-stream
(probably derived from something "emarsden" posted somewhere, but i
can't find it) which specializes the fd-stream class and adds a
connection timeout. IIRC This integrates with the SERVE-EVENT
abstraction which is a co-ooperative multiprocessing layer about select,
and the implementation is tied to SERVE-EVENT, if the fd is not usable
(data does not arrive on it in the given time), it raises a timeout
error.
On second thoughts I don't think it will solve your problem.
Let's talk about the problem, which I think we did not do too much yet.
The exercise is not an NNTP client, but a server. I think the server
should not be totally naive when it comes to waiting for peers to talk.
(Even though I believe a denial-of-service might be a too-difficult
problem to solve. I don't even know what ``perfect'' protection against
such attacks would be.) I would imagine that TCP already solves the
timeout problem, but still --- I feel like it should be an easy exercise
for a program to handle that itself. So, my exercise was to read a file
descriptor with a timeout --- give me your command or die.
Post by Madhu
I just thought this was the way to approach the problem: call READ and
handle a timeout condition (from the implementation)
I think the CMUCL code base still has the code for a netnews client for
hemlock, though it doesn't use this strategy. You can see the code from
1980, (which has not yet been deleted by some open source who has been
hired to delete it) here
https://gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/hemlock/netnews.lisp
(or wihout js)
https://gitlab.common-lisp.net/cmucl/cmucl/-/raw/master/src/hemlock/netnews.lisp
Thanks. That's fun reading.
Post by Madhu
I checked trivial-nntp (probably in quicklisp) but it just wraps
usocket and doesnt deal with timeouts. iolib also doesnt seem to
expose any setsock SO_RCVTIMEO, though it can probably used, i havent
figured it out, i'll have to look again later.
Thanks for looking into it.

The strategy that I would apply to the exercise is to use a TCP server.
I suppose usocket implies sockets but my exercise expects to handle just
standard input, so I guess I could not really use a socket library for
anything.
Lawrence D'Oliveiro
2024-01-06 23:35:39 UTC
Permalink
Post by Julieta Shem
So, my exercise was to read a file
descriptor with a timeout --- give me your command or die.
Typically you will have multiple concurrent client connections going on.
So you want an event loop that monitors all connections. Each one will
have a last-active timer. The poll(2) call will have a timeout that
corresponds to the earliest of these timer expirations. So if you don’t
get any activity in that time (or even if you do), you start disconnecting
idle clients.

I have a worked example of this, in Python using asyncio here
<https://gitlab.com/ldo/chunk_protocol_example>.
Julieta Shem
2024-01-07 01:43:54 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Julieta Shem
So, my exercise was to read a file
descriptor with a timeout --- give me your command or die.
Typically you will have multiple concurrent client connections going on.
So you want an event loop that monitors all connections. Each one will
have a last-active timer. The poll(2) call will have a timeout that
corresponds to the earliest of these timer expirations. So if you don’t
get any activity in that time (or even if you do), you start disconnecting
idle clients.
The exercise is much easier than that --- it's an NNTP server, so the
service doesn't need to handle clients in a centralized manner. The TCP
server will spawn the nntpd, which will only handle a single client.
Madhu
2024-01-07 02:31:48 UTC
Permalink
Post by Julieta Shem
Post by Lawrence D'Oliveiro
Post by Julieta Shem
So, my exercise was to read a file
descriptor with a timeout --- give me your command or die.
Typically you will have multiple concurrent client connections going
on. So you want an event loop that monitors all connections. Each
one will have a last-active timer. The poll(2) call will have a
timeout that corresponds to the earliest of these timer
expirations. So if you don’t get any activity in that time (or even
if you do), you start disconnecting idle clients.
The exercise is much easier than that --- it's an NNTP server, so the
service doesn't need to handle clients in a centralized manner. The
TCP server will spawn the nntpd, which will only handle a single
client.
TCPD is a nice abstraction but in my mind there is a conflict, bringing
in lisp, also brings an unbearable urge to manage the file descriptors
from lisp in a long running stateful lisp process, Personally I never
got over this particular dissonance ("If I use lisp I _have_ to take
advantage of this feature long-running-process -- or bust"), and prefer
short small executables programs for processes. [OTOH I remember cl-http
on buggy linux in 1996 which would result in a number of TCP connections
which were never cleaned up for 2 days]

Likewise "If I use Lisp I _have_ to take advantage of the stream
abstraction and all the goodies of its stream design". In lisp stdin
(when it is also *terminal-io*) is special because it is an interactive
stream. You can call PEEK on it to see if there is data to be read and
and handle some aspects of interactivity primitively through that. CL
defines INTERACTIVE-STREAM-P and extensions like gray streams purport to
support it. but when you get stdin from a fdstream or TCPstream, it
isn't really special.
Julieta Shem
2024-01-07 05:55:44 UTC
Permalink
Post by Madhu
Post by Julieta Shem
Post by Lawrence D'Oliveiro
Post by Julieta Shem
So, my exercise was to read a file
descriptor with a timeout --- give me your command or die.
Typically you will have multiple concurrent client connections going
on. So you want an event loop that monitors all connections. Each
one will have a last-active timer. The poll(2) call will have a
timeout that corresponds to the earliest of these timer
expirations. So if you don’t get any activity in that time (or even
if you do), you start disconnecting idle clients.
The exercise is much easier than that --- it's an NNTP server, so the
service doesn't need to handle clients in a centralized manner. The
TCP server will spawn the nntpd, which will only handle a single
client.
TCPD is a nice abstraction but in my mind there is a conflict, bringing
in lisp, also brings an unbearable urge to manage the file descriptors
from lisp in a long running stateful lisp process, Personally I never
got over this particular dissonance ("If I use lisp I _have_ to take
advantage of this feature long-running-process -- or bust"), and prefer
short small executables programs for processes. [OTOH I remember cl-http
on buggy linux in 1996 which would result in a number of TCP connections
which were never cleaned up for 2 days]
Likewise "If I use Lisp I _have_ to take advantage of the stream
abstraction and all the goodies of its stream design". In lisp stdin
(when it is also *terminal-io*) is special because it is an interactive
stream. You can call PEEK on it to see if there is data to be read and
and handle some aspects of interactivity primitively through that. CL
defines INTERACTIVE-STREAM-P and extensions like gray streams purport to
support it. but when you get stdin from a fdstream or TCPstream, it
isn't really special.
I don't understand. The program is being written to handle a TCP
connection. Terminals are not going to be connected to it. How would
you handle that?

I am thankful I can write the program with my keyboard attached to it
and then later naturally switch it all to a TCP connection. Isn't that
great?
Madhu
2024-01-07 07:10:25 UTC
Permalink
Post by Julieta Shem
Post by Madhu
TCPD is a nice abstraction but in my mind there is a conflict, bringing
in lisp, also brings an unbearable urge to manage the file descriptors
from lisp in a long running stateful lisp process, Personally I never
[...]
Post by Julieta Shem
Post by Madhu
Likewise "If I use Lisp I _have_ to take advantage of the stream
abstraction and all the goodies of its stream design". In lisp stdin
(when it is also *terminal-io*) is special because it is an interactive
stream. You can call PEEK on it to see if there is data to be read and
and handle some aspects of interactivity primitively through that. CL
defines INTERACTIVE-STREAM-P and extensions like gray streams purport to
support it. but when you get stdin from a fdstream or TCPstream, it
isn't really special.
I don't understand. The program is being written to handle a TCP
connection. Terminals are not going to be connected to it. How would
you handle that?
I wouldn't. I just meant that using lisp for a program that reads
standard input and writes to standard output would not be able to use
the (admittedly limited) interactive features of interactive-stream-p
and peek, which were designed to handle interactivity.

Once you have to set non-blocking on the FD you've already breached the
abstraction barrier (that limits you dealing solely with stdin
stdout). When you want to do timeouts on the input, that is a another
breach. You're no longer programming within the niceties where you
program just reads stdin and handles stdout and leave TCPD to handle all
the network and timeout issues. So now there is a sort of square/round
peg/hole.
Post by Julieta Shem
I am thankful I can write the program with my keyboard attached to it
and then later naturally switch it all to a TCP connection. Isn't that
great?
Lawrence D'Oliveiro
2024-01-05 21:57:38 UTC
Permalink
Post by Madhu
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Remember the main point of select/poll, in that it lets you non-
deterministically wait on a number of file descriptors at once, and
respond immediately to the first one that has any I/O to do.
Julieta Shem
2024-01-05 23:57:43 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Madhu
I think a better way to approach this is to (somehow) set a
stream-timout on the stream and have read-line (on your special stream)
return nil (or a supplied on-timeout value) if there is no data on the
stream. your lisp code in your thread would still deal with a blocking
read.
Remember the main point of select/poll, in that it lets you non-
deterministically wait on a number of file descriptors at once, and
respond immediately to the first one that has any I/O to do.
I think Madhu's mind is on handling just a single file descriptor but
with a timeout (and with all the conform of a stream).
Julieta Shem
2024-01-06 00:15:12 UTC
Permalink
Post by Julieta Shem
Post by Kaz Kylheku
Post by Julieta Shem
which seems helpful, but with no examples. Given that I could write
such program in C using select(2) or poll(2), would there be anything
you could say here that would give me a direction? Thank you! Any
interesting publication that I so far failed to have found? (For
instance, I have Peter Seibel's Practical Common Lisp, but it doesn't
seem to help me with this exercise.)
One thing to watch out for, in any language, is if you're reading
from high level buffered streams but trying to use select or poll.
(This goes for C stdio also.)
You don't want to be accidentally blocking in select or poll
while the stream contains unread, buffered data (already transferred
from the operating system descriptor to user space).
That can happen if the stream reads more data than your program
requested. E.g. you're looking for one byte, and poll. The stream
initially has nothing so this is right. Poll unblocks when data
becomes available, so you read the one byte from the stream. But suppose
that the stream actually reads 500 bytes from the OS, giving you 1 and
buffering 499. After that, you have to read the available 499 bytes
from the stream before calling poll (unless poll is aware of streams,
rather than just a thin wrapper for the POSIX function.)
You have to put the underlying descriptor into non-blocking mode
(even if you're only reading from that one). Then whenever it polls
positive, read all available data from the stream until it hits an
error from the file descriptor with EWOULDBLOCK.
I need to learn to put file descriptors in non-blocking mode. Thanks
for reminding me.
It turns out SB-POSIX provides fcntl().

--8<---------------cut here---------------start------------->8---
(require 'sb-posix)
(defun ndelay-on (fd)
(sb-posix:fcntl fd
sb-posix:f-setfl
(logior sb-posix:o-nonblock
(sb-posix:fcntl fd sb-posix:f-getfl 0))))
--8<---------------cut here---------------end--------------->8---

Reading the source of SB-UNIX, I managed to learn how to invoke
UNIX-READ. So I was able to write the following procedure. It puts the
FD into non-blocking mode, then it allocates a buffer using SB-ALIEN,
whose memory I have no idea who is freeing. I had to learn how to close
the c-string. Small buffer of size 10 on purpose.

--8<---------------cut here---------------start------------->8---
(defun ndelay-read-from-fd (fd)
(ndelay-on fd)
(let ((buf (make-alien (array char 10))))
(multiple-value-bind (nbytes-or-nil errno)
;; spare a byte for closing the c-string
(sb-unix:unix-read fd (alien-sap buf) (1- 10))
(cond ((null nbytes-or-nil) (values nil errno))
(t
;; close the c-string
(setf (deref (deref buf 0) nbytes-or-nil) 0)
(values nbytes-or-nil buf))))))
--8<---------------cut here---------------end--------------->8---

As a sample program, I wrote ndelay-read-line. The ``read-line'' is
surely illusory because I'm not parsing lines at all. Maybe Madhu is
going to wrap this code with a stream ``somehow''. :-)

--8<---------------cut here---------------start------------->8---
(defun ndelay-read-line ()
(defun strerror (n) (if (= n 11) "EGAIN" "EDONTKNOW"))
(multiple-value-bind (nbytes-or-nil alien-or-errno) (ndelay-read-from-fd 0)
(cond ((and (null nbytes-or-nil))
(format t "errno = ~a~%" (strerror alien-or-errno)))
((= 0 nbytes-or-nil)
(format t "eof (read returned 0 bytes)~%"))
(t (format t "~a..."
(cast alien-or-errno c-string))
(ndelay-read-line)))))

;; main =
(ndelay-read-line)
--8<---------------cut here---------------end--------------->8---

We can call this a second exercise. I suppose I have enough for the
third exercise of polling the FD with a timeout and be sure we will not
block if the data is not a full line. It will surely take more
abstraction. (Eventually I need to learn what is being allocated and
who is to free it and when.)

Someone pointed me out to CFFI at

https://cffi.common-lisp.dev/

with the promise of being more expressive than SB-ALIEN.
Loading...