* How to gradually write a procedure using Guile?
@ 2024-05-03 0:24 Tomas Volf
2024-05-03 8:45 ` Jérémy Korwin-Zmijowski
0 siblings, 1 reply; 4+ messages in thread
From: Tomas Volf @ 2024-05-03 0:24 UTC (permalink / raw)
To: guile-user
[-- Attachment #1: Type: text/plain, Size: 1571 bytes --]
Hello,
I am looking for a workflow advice. I am using Emacs with Geiser.
I am trying to write couple of procedures that are very imperative and I am not
sure how to do that nicely in REPL. For example, let us assume I have a
procedure of following character:
(define (foo)
(let* ((x (bar-x))
(y (bar-y x)))
(step1 x)
(step2 y)
(step3 x y)
...))
Now, each step can be a procedure call, of just few expressions. Now I would
like to write the additional steps while utilizing REPL somehow, but I am not
sure what is an efficient way.
Can I somehow run just to the `...' and get a REPL there so that I could C-x C-e
the steps within the let* (for x and y)? Should I just (temporarily)
(define x (bar-x))
(define y (bar-y x))
in the REPL so that I can use C-x C-e on the steps? I expect that to get messy
once the lets start nesting for example.
How do you do it? Are there any resources (blog posts, toots, videos, ...)
regarding guile developer's workflow? I did read few, and I (think I) know the
fundamentals of Geiser and the REPL, but I am straggling a bit in this case not
to fall back to the "normal" way of "write a function, run it whole against a
test". Since this is Scheme, and I *can* evaluate single expressions in the
procedure body, I would like to use that to my advantage. Somehow.
I realize this is a very open-ended question/email.
Have a nice day,
Tomas Volf
--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: How to gradually write a procedure using Guile?
2024-05-03 0:24 How to gradually write a procedure using Guile? Tomas Volf
@ 2024-05-03 8:45 ` Jérémy Korwin-Zmijowski
2024-05-03 11:49 ` Dr. Arne Babenhauserheide
0 siblings, 1 reply; 4+ messages in thread
From: Jérémy Korwin-Zmijowski @ 2024-05-03 8:45 UTC (permalink / raw)
To: guile-user
Hi Tomas !
You have to make choices in the code weather you want to leverage the
REPL or not. That's fine. I incentivize you to try different approaches
and see how it feels while you work. So you can make your choices based
on actual experience.
What comes to my mind right now is pretty close to what you imagined.
You could keep your code as is and on a new line evaluate `(foo)` every
time you make a relevant progress inside the `foo` definition. Write the
first step, see if you can get the right result here, then go on the
next step or fix your code.… This is my go to approach (which I follow
in a TDD manner).
Or you could define `x` and `y` in the REPL as you suggested it and then
write definitions of your steps, one at a time (the original question
remains, how to write the step interactively?). Then, when all the steps
are working, try to integrate them in a `foo` procedure and see how it goes…
Jérémy
Le 03/05/2024 à 02:24, Tomas Volf a écrit :
> Hello,
>
> I am looking for a workflow advice. I am using Emacs with Geiser.
>
> I am trying to write couple of procedures that are very imperative and I am not
> sure how to do that nicely in REPL. For example, let us assume I have a
> procedure of following character:
>
> (define (foo)
> (let* ((x (bar-x))
> (y (bar-y x)))
> (step1 x)
> (step2 y)
> (step3 x y)
> ...))
>
> Now, each step can be a procedure call, of just few expressions. Now I would
> like to write the additional steps while utilizing REPL somehow, but I am not
> sure what is an efficient way.
>
> Can I somehow run just to the `...' and get a REPL there so that I could C-x C-e
> the steps within the let* (for x and y)? Should I just (temporarily)
>
> (define x (bar-x))
> (define y (bar-y x))
>
> in the REPL so that I can use C-x C-e on the steps? I expect that to get messy
> once the lets start nesting for example.
>
> How do you do it? Are there any resources (blog posts, toots, videos, ...)
> regarding guile developer's workflow? I did read few, and I (think I) know the
> fundamentals of Geiser and the REPL, but I am straggling a bit in this case not
> to fall back to the "normal" way of "write a function, run it whole against a
> test". Since this is Scheme, and I *can* evaluate single expressions in the
> procedure body, I would like to use that to my advantage. Somehow.
>
> I realize this is a very open-ended question/email.
>
> Have a nice day,
> Tomas Volf
>
> --
> There are only two hard things in Computer Science:
> cache invalidation, naming things and off-by-one errors.
--
Jérémy Korwin-Zmijowski
GPG: AD1945EC4F03FC79
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: How to gradually write a procedure using Guile?
2024-05-03 8:45 ` Jérémy Korwin-Zmijowski
@ 2024-05-03 11:49 ` Dr. Arne Babenhauserheide
2024-05-16 18:25 ` Tomas Volf
0 siblings, 1 reply; 4+ messages in thread
From: Dr. Arne Babenhauserheide @ 2024-05-03 11:49 UTC (permalink / raw)
To: Jérémy Korwin-Zmijowski; +Cc: guile-user
[-- Attachment #1: Type: text/plain, Size: 4238 bytes --]
Hi Thomas,
I usually work by redefining the whole procedure and running it.
Typically I start with the minimal procedure
(define (hello) #f)
Then — for nicely testable procedures — I add a doctest and get it to
run:
(define (hello)
"Say hello."
#((tests
('answers
(test-equal "Hello" (hello)))))
"Hello")
Then I run the tests from Emacs:
;; eval in emacs: (progn (defun test-this-file () (interactive) (save-current-buffer) (async-shell-command "./hello.scm --test")) (local-set-key (kbd "<f9>") 'test-this-file))
(this relies on https://hg.sr.ht/~arnebab/wisp/browse/examples/doctests.scm)
For a game I work on with my kids, I made the module non-declarative,
so I could replace all bindings at runtime:
(define-module (hello)
#:export (hello)
#:declarative? #f)
Then I could start the kooperative repl server:
(import (system repl coop-server))
(define (update dt
(poll-coop-repl-server repl)))
This relies on Chickadee. See https://notabug.org/ZelphirKaltstahl/guile-chickadee-examples/src/master/example-03-live-coding-repl/main.scm#L15
Then I could open that server, in the repl use ,m (hello) to enter the
module, and replace the procedure at runtime to see the effect.
I replace it by simply pasting the procedure code into the REPL.
(after ,m (hello))
Best wishes,
Arne
Jérémy Korwin-Zmijowski <jeremy@korwin-zmijowski.fr> writes:
> Hi Tomas !
>
> You have to make choices in the code weather you want to leverage the
> REPL or not. That's fine. I incentivize you to try different
> approaches and see how it feels while you work. So you can make your
> choices based on actual experience.
>
> What comes to my mind right now is pretty close to what you imagined.
>
> You could keep your code as is and on a new line evaluate `(foo)`
> every time you make a relevant progress inside the `foo` definition.
> Write the first step, see if you can get the right result here, then
> go on the next step or fix your code.… This is my go to approach
> (which I follow in a TDD manner).
>
> Or you could define `x` and `y` in the REPL as you suggested it and
> then write definitions of your steps, one at a time (the original
> question remains, how to write the step interactively?). Then, when
> all the steps are working, try to integrate them in a `foo` procedure
> and see how it goes…
>
> Jérémy
>
> Le 03/05/2024 à 02:24, Tomas Volf a écrit :
>> Hello,
>>
>> I am looking for a workflow advice. I am using Emacs with Geiser.
>>
>> I am trying to write couple of procedures that are very imperative and I am not
>> sure how to do that nicely in REPL. For example, let us assume I have a
>> procedure of following character:
>>
>> (define (foo)
>> (let* ((x (bar-x))
>> (y (bar-y x)))
>> (step1 x)
>> (step2 y)
>> (step3 x y)
>> ...))
>>
>> Now, each step can be a procedure call, of just few expressions. Now I would
>> like to write the additional steps while utilizing REPL somehow, but I am not
>> sure what is an efficient way.
>>
>> Can I somehow run just to the `...' and get a REPL there so that I could C-x C-e
>> the steps within the let* (for x and y)? Should I just (temporarily)
>>
>> (define x (bar-x))
>> (define y (bar-y x))
>>
>> in the REPL so that I can use C-x C-e on the steps? I expect that to get messy
>> once the lets start nesting for example.
>>
>> How do you do it? Are there any resources (blog posts, toots, videos, ...)
>> regarding guile developer's workflow? I did read few, and I (think I) know the
>> fundamentals of Geiser and the REPL, but I am straggling a bit in this case not
>> to fall back to the "normal" way of "write a function, run it whole against a
>> test". Since this is Scheme, and I *can* evaluate single expressions in the
>> procedure body, I would like to use that to my advantage. Somehow.
>>
>> I realize this is a very open-ended question/email.
>>
>> Have a nice day,
>> Tomas Volf
>>
>> --
>> There are only two hard things in Computer Science:
>> cache invalidation, naming things and off-by-one errors.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 1125 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: How to gradually write a procedure using Guile?
2024-05-03 11:49 ` Dr. Arne Babenhauserheide
@ 2024-05-16 18:25 ` Tomas Volf
0 siblings, 0 replies; 4+ messages in thread
From: Tomas Volf @ 2024-05-16 18:25 UTC (permalink / raw)
To: Dr. Arne Babenhauserheide; +Cc: Jérémy Korwin-Zmijowski, guile-user
[-- Attachment #1: Type: text/plain, Size: 2003 bytes --]
Hi,
(and apologized for slightly later response)
On 2024-05-03 13:49:35 +0200, Dr. Arne Babenhauserheide wrote:
> [..]
>
> Jérémy Korwin-Zmijowski <jeremy@korwin-zmijowski.fr> writes:
>
> > [..]
Thank you both for the useful tips. I experimented with the workflow for few
weeks and, with your suggestions, reached the following process.
For simple, (semi-)pure functions, that can be easily tested in REPL, I evaluate
the whole definition and run in manually in the REPL to see the result. I
already was doing this before, and it seems to work fine, so I am sticking to
it.
For long, totally non-pure functions (motivation for my original email), I
accepted the fact that I will have to modify the code to make it REPL-friendly.
So, for example, instead of
(define (foo)
(let ((bar 1))
...))
I am using
(define (foo)
(define bar 1)
...)
While less elegant, it allows me to do the setup once (evaluating the procedure
one expression after another) in the REPL, and then I can just continue adding
new expressions while evaluating them one by one. The end result is slightly
harder to reason about, but still worth it in my case[0].
The obvious down-side is the top-level pollution, but with bit of care it seems
to be manageable. Sometimes, when I mess up, it requires restarting the REPL,
but I can skip the expensive parts of the setup, so it is pretty fast.
Thank you both again and have a nice day,
Tomas Volf
0: The full procedure is an install script for a server and runs for about 45
minutes having many side-effects, like installing packages, compiling
external programs and configuring the system. Yes, it would be possible to
split it into multiple separate procedures, but the state passing gets
annoying quickly and the resulting code is less straight forward.
--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-05-16 18:25 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-03 0:24 How to gradually write a procedure using Guile? Tomas Volf
2024-05-03 8:45 ` Jérémy Korwin-Zmijowski
2024-05-03 11:49 ` Dr. Arne Babenhauserheide
2024-05-16 18:25 ` Tomas Volf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).