(define (thunk) (lambda (x) x)) should be a compile error?

  • Done
  • quality assurance status badge
Details
4 participants
  • Joshua Branson
  • Ricardo Wurmus
  • Stefan Israelsson Tampe
  • tomas
Owner
unassigned
Submitted by
Joshua Branson
Severity
normal
J
J
Joshua Branson wrote on 21 Jan 2021 03:04
(define (thunk) (lambda (x) x)) should be a compile error?
(address . bug-guile@gnu.org)
87h7nb3v0g.fsf@dismail.de
Hello!

Consider this bit of simple code:

#+BEGIN_SRC scheme

(define (thunk)
(lambda (x)
x))

(thunk) ;; works ok, I guess.
(thunk "hello world!\n") ;; runtime error

;;; <stdin>:1074:0: warning: possibly wrong number of arguments to `thunk'
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Wrong number of arguments to #<procedure thunk ()>

Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
#+END_SRC

Guile will compile this program seemingly with no error. Guile will
correctly report at runtime that procedure '(thunk "hello world!\n")'
takes no arguments, but it's lambda accepts 1 argument. Would it be
possible to report this error at compile time? Would that be
advantageous?

I personally can consider one time where reporting this error at
compile time would be advantageous. Here's how I found out about this
error. (would ya'll call this an error/bug/feature)?

#+BEGIN_SRC scheme
(use-modules
(srfi srfi-9))

(define-record-type <lunch>
(make-lunch food duration location)
lunch?
(food lunch-food)
(duration lunch-duration)
(location lunch-location))

(define dine-out
(make-lunch "pizza" "30 min" "downtown"))

;; maybe this should be a syntax error instead of a runtime error?
(define (list-lunch)
(match-lambda (($ <lunch> food duration location )
(list food duration location))))

;; this is the proper way to do it.
(define list-lunch
(match-lambda (($ <lunch> food duration location )
(list food duration location))))
#+END_SRC

I eventually discovered what the issue was, but I had to run the code
to discover it.

I don't know if this is a silly bug report. If it is, sorry for the
noise.

Thanks,

Joshua

P.S. Guile's error messages at compile time and runtime are super
well written. Thanks!
R
R
Ricardo Wurmus wrote on 21 Jan 2021 14:56
Re: bug#46014: (define (thunk) (lambda (x) x)) should be a compile error?
(name . Joshua Branson)(address . jbranso@dismail.de)
87bldil7fo.fsf@elephly.net
Hi,

Toggle quote (24 lines)
> Consider this bit of simple code:
>
> #+BEGIN_SRC scheme
>
> (define (thunk)
> (lambda (x)
> x))
>
> (thunk) ;; works ok, I guess.
> (thunk "hello world!\n") ;; runtime error
>
> ;;; <stdin>:1074:0: warning: possibly wrong number of arguments to `thunk'
> ice-9/boot-9.scm:1669:16: In procedure raise-exception:
> Wrong number of arguments to #<procedure thunk ()>
>
> Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
> #+END_SRC
>
> Guile will compile this program seemingly with no error. Guile will
> correctly report at runtime that procedure '(thunk "hello world!\n")'
> takes no arguments, but it's lambda accepts 1 argument. Would it be
> possible to report this error at compile time? Would that be
> advantageous?

This is not a bug. What you call “thunk” here is a procedure that
returns a procedure. That’s very common and is often done to delay
evaluation.

It is in fact an error to call the procedure “thunk” with an argument.
It doesn’t matter that it happens to return a procedure that *can* take
an argument. The procedure it returns is just like any other value,
though, and isn’t inspected any further.

That said, it is not true that Guile will compile this without a
complaint. I dumped your code snippet in a file foo.scm and
compiled it:

Toggle snippet (5 lines)
guild compile foo.scm
foo.scm:6:0: warning: wrong number of arguments to `thunk'
wrote `/home/rekado/.cache/guile/ccache/3.0-LE-8-4.4/home/rekado/dev/gx/gwl/foo.scm.go'

Isn’t that exactly what you’re asking for?

--
Ricardo
S
S
Stefan Israelsson Tampe wrote on 21 Jan 2021 19:16
(name . Ricardo Wurmus)(address . rekado@elephly.net)
CAGua6m1v-yti0z-8MxAC2gPDSkrmydx_EckRjeLMK+bCiKR0bQ@mail.gmail.com
I will note that paying attention to those warnings is super heolpful in
programming in guile. I miss these warnings
alot when doing python programming.In a sense with these warnings you get
close to static typechecking programming
languages and scheme/guile is really a nice mix of dynamic and static typing

On Thu, Jan 21, 2021 at 2:58 PM Ricardo Wurmus <rekado@elephly.net> wrote:

Toggle quote (56 lines)
>
> Hi,
>
> > Consider this bit of simple code:
> >
> > #+BEGIN_SRC scheme
> >
> > (define (thunk)
> > (lambda (x)
> > x))
> >
> > (thunk) ;; works ok, I guess.
> > (thunk "hello world!\n") ;; runtime error
> >
> > ;;; <stdin>:1074:0: warning: possibly wrong number of arguments to
> `thunk'
> > ice-9/boot-9.scm:1669:16: In procedure raise-exception:
> > Wrong number of arguments to #<procedure thunk ()>
> >
> > Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
> > #+END_SRC
> >
> > Guile will compile this program seemingly with no error. Guile will
> > correctly report at runtime that procedure '(thunk "hello world!\n")'
> > takes no arguments, but it's lambda accepts 1 argument. Would it be
> > possible to report this error at compile time? Would that be
> > advantageous?
>
> This is not a bug. What you call “thunk” here is a procedure that
> returns a procedure. That’s very common and is often done to delay
> evaluation.
>
> It is in fact an error to call the procedure “thunk” with an argument.
> It doesn’t matter that it happens to return a procedure that *can* take
> an argument. The procedure it returns is just like any other value,
> though, and isn’t inspected any further.
>
> That said, it is not true that Guile will compile this without a
> complaint. I dumped your code snippet in a file foo.scm and
> compiled it:
>
> --8<---------------cut here---------------start------------->8---
> guild compile foo.scm
> foo.scm:6:0: warning: wrong number of arguments to `thunk'
> wrote
> `/home/rekado/.cache/guile/ccache/3.0-LE-8-4.4/home/rekado/dev/gx/gwl/foo.scm.go'
> --8<---------------cut here---------------end--------------->8---
>
> Isn’t that exactly what you’re asking for?
>
> --
> Ricardo
>
>
>
>
Attachment: file
J
J
Joshua Branson wrote on 21 Jan 2021 21:09
(name . Ricardo Wurmus)(address . rekado@elephly.net)
87eeie2gs1.fsf@dismail.de
Ricardo Wurmus <rekado@elephly.net> writes:

Toggle quote (45 lines)
> Hi,
>
>> Consider this bit of simple code:
>>
>> #+BEGIN_SRC scheme
>>
>> (define (thunk)
>> (lambda (x)
>> x))
>>
>> (thunk) ;; works ok, I guess.
>> (thunk "hello world!\n") ;; runtime error
>>
>> ;;; <stdin>:1074:0: warning: possibly wrong number of arguments to `thunk'
>> ice-9/boot-9.scm:1669:16: In procedure raise-exception:
>> Wrong number of arguments to #<procedure thunk ()>
>>
>> Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
>> #+END_SRC
>>
>> Guile will compile this program seemingly with no error. Guile will
>> correctly report at runtime that procedure '(thunk "hello world!\n")'
>> takes no arguments, but it's lambda accepts 1 argument. Would it be
>> possible to report this error at compile time? Would that be
>> advantageous?
>
> This is not a bug. What you call “thunk” here is a procedure that
> returns a procedure. That’s very common and is often done to delay
> evaluation.
>
> It is in fact an error to call the procedure “thunk” with an argument.
> It doesn’t matter that it happens to return a procedure that *can* take
> an argument. The procedure it returns is just like any other value,
> though, and isn’t inspected any further.
>
> That said, it is not true that Guile will compile this without a
> complaint. I dumped your code snippet in a file foo.scm and
> compiled it:
>
> guild compile foo.scm
> foo.scm:6:0: warning: wrong number of arguments to `thunk'
> wrote `/home/rekado/.cache/guile/ccache/3.0-LE-8-4.4/home/rekado/dev/gx/gwl/foo.scm.go'
>
> Isn’t that exactly what you’re asking for?

Gotcha. Thanks for explaining! I suppose what I meant to say is,
should guile refuse to compile the above? In other languages, like C I
suppose, writing a function simultaneous with one and two arguments
would refuse to compile. The compiler would make you fix the code.

Should guile do this as well?

When I look at

#+BEGIN_SRC scheme
(define (thunk)
(lambda (x)
x))
#+END_SRC

or

#+BEGIN_SRC scheme
(use-modules
(srfi srfi-9))

(define-record-type <lunch>
(make-lunch food duration location)
lunch?
(food lunch-food)
(duration lunch-duration)
(location lunch-location))

(define dine-out
(make-lunch "pizza" "30 min" "downtown"))

;; maybe this should refuse to compile?
(define (list-lunch)
(match-lambda (($ <lunch> food duration location )
(list food duration location))))
#+END_SRC

My thought is, this is clearly a mistake. This person needs to change
the above code.

Thanks,

Joshua

P.S. I'm not a scheme expert. I'm only reporting this, because I
recently read a blog post about free software users rarely report
perceived issues. I'm just trying to be helpful. :) Thanks for the
speedy response time.

--
Joshua Branson (joshuaBPMan in #guix)
Sent from Emacs and Gnus
"You can have whatever you want, as long as you help
enough other people get what they want." - Zig Ziglar
J
J
Joshua Branson wrote on 21 Jan 2021 21:11
(name . Stefan Israelsson Tampe)(address . stefan.itampe@gmail.com)
877do62gpe.fsf@dismail.de
Stefan Israelsson Tampe <stefan.itampe@gmail.com> writes:

Toggle quote (4 lines)
> I will note that paying attention to those warnings is super heolpful in programming in guile. I miss these warnings
> alot when doing python programming.In a sense with these warnings you get close to static typechecking programming
> languages and scheme/guile is really a nice mix of dynamic and static typing

I couldn't agree more! Guile's warning/error messages are super
helpful! Taking the time to read those messages really help you catch
issues. Often times before you run the program!

--
Joshua Branson (joshuaBPMan in #guix)
Sent from Emacs and Gnus
"You can have whatever you want, as long as you help
enough other people get what they want." - Zig Ziglar
R
R
Ricardo Wurmus wrote on 21 Jan 2021 23:27
(name . Joshua Branson)(address . jbranso@dismail.de)
878s8mkjsk.fsf@elephly.net
Hi Joshua,

Toggle quote (8 lines)
> When I look at
>
> #+BEGIN_SRC scheme
> (define (thunk)
> (lambda (x)
> x))
> #+END_SRC

[…]

Toggle quote (3 lines)
> My thought is, this is clearly a mistake. This person needs to change
> the above code.

How is this clearly a mistake? The definition of “thunk” above is
perfectly fine and also common.

The above is equivalent to

(define thunk
(lambda ()
(lambda (x) x)))

And that’s really okay and can be desired. The problem is not with
this definition. If someone calls this wrongly, well, that’s a problem
with the caller. And Guile’s compiler does tell you that you are
probably wrong in calling “thunk” with an argument.

Do I understand you correctly that you would like this warning to be an
error instead?

Toggle quote (5 lines)
> Gotcha. Thanks for explaining! I suppose what I meant to say is,
> should guile refuse to compile the above? In other languages, like C I
> suppose, writing a function simultaneous with one and two arguments
> would refuse to compile. The compiler would make you fix the code.

Let me address this separately. In Scheme you *can* define a procedure
that takes a different number of arguments. Here’s one example from the
manual:

(define (make-accum n)
(case-lambda
(() n)
((m) (set! n (+ n m)) n)))

(define a (make-accum 20))
(a) ⇒ 20
(a 10) ⇒ 30
(a) ⇒ 30

“case-lambda” specifies a procedure that can take arguments in as many
different shapes as there are clauses. Here there are two clauses: one
for the case where no arguments are provided and another where one
argument (bound to “m”) is provided.

Furthermore, you can see here that this is a higher order procedure, as
“make-accum” takes an argument and returns a procedure (the
case-lambda).

Another example, also from the manual, is this:

(lambda* (start #:optional (end (+ 10 start)))
(do ((i start (1+ i)))
((> i end))
(display i)))

This procedure takes one or two arguments.

--
Ricardo
J
J
Joshua Branson wrote on 22 Jan 2021 15:47
(name . Ricardo Wurmus)(address . rekado@elephly.net)
87o8hhyqo3.fsf@dismail.de
Ricardo Wurmus <rekado@elephly.net> writes:

Toggle quote (18 lines)
> Hi Joshua,
>
>> When I look at
>>
>> #+BEGIN_SRC scheme
>> (define (thunk)
>> (lambda (x)
>> x))
>> #+END_SRC
>
> […]
>
>> My thought is, this is clearly a mistake. This person needs to change
>> the above code.
>
> How is this clearly a mistake? The definition of “thunk” above is
> perfectly fine and also common.

Thanks again for responding. I'm still learning scheme, and it's cool
that this email chain has helped clarify some things. :)

Ahh. I see now that the proper way to call thunk is to do this:
((thunk) "the")
$1 = "the"

I had assumed that every time one called thunk, it would result in a
runtime error. I did not realize that there was a way to properly call
thunk. Wow. Scheme is truly impressive.

Interestingly, I had wrongly assumed that

#+BEGIN_SRC scheme
(thunk "test\n") ;; I assumed program execution would stop here
(display "Hello World\n")
#+END_SRC

program execution would stop at (thunk "test\n"). But it actually
caries on with execution of the program:

#+BEGIN_SRC scheme
<stdin>:5:0: warning: possibly wrong number of arguments to `thunk'
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Wrong number of arguments to #<procedure thunk ()>

Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
Hello World
#+END_SRC

I'm not certain if it is a good or bad thing that scheme continues
program execution after a runtime error. I know I've built toy web
applications in guile... Sometimes the input to a form is bad data, and
the web-server emits a runtime error, but keeps running. Maybe it would
be better if the program halted on every runtime error. It would be a
way of saying, "Hey your web-server hasn't really handled dealing with
bad data properly. You need to fix it, because I will crash at every
run time error."

For fun I also thought about how else I could write thunk. Continue
reading at your own peril.

#+BEGIN_SRC scheme
;; This procedure doesn't work the way I thought it would. The way to
;; print a string with this procedure is to do this:
;; ((thunk "the") "the")
(define (thunk x)
(lambda (x) x))

;; obvious. This is equivalent to
;; (define (thunk x) x)
(define thunk
(lambda (x)
x))

;; This ones nice because neither (thunk) nor (thunk "the") result in a
;; runtime error.
(define* (thunk #:optional x)
x)

(define* (thunk #:optional x)
(lambda* (#:optional x)
x))
#+END_SRC

Are there some other really weird and convoluted ways of writing thunk
that I'm missing? I'm guessing so.

Toggle quote (15 lines)
>
> The above is equivalent to
>
> (define thunk
> (lambda ()
> (lambda (x) x)))
>
> And that’s really okay and can be desired. The problem is not with
> this definition. If someone calls this wrongly, well, that’s a problem
> with the caller. And Guile’s compiler does tell you that you are
> probably wrong in calling “thunk” with an argument.
>
> Do I understand you correctly that you would like this warning to be an
> error instead?

No. Not anymore. Before I had assumed that any invocation of thunk
would result in a runtime error. If that was the case, I figured guile
should refuse to compile it. Now I realize that ((thunk "the") "the")
works.

Toggle quote (29 lines)
>
>> Gotcha. Thanks for explaining! I suppose what I meant to say is,
>> should guile refuse to compile the above? In other languages, like C I
>> suppose, writing a function simultaneous with one and two arguments
>> would refuse to compile. The compiler would make you fix the code.
>
> Let me address this separately. In Scheme you *can* define a procedure
> that takes a different number of arguments. Here’s one example from the
> manual:
>
> (define (make-accum n)
> (case-lambda
> (() n)
> ((m) (set! n (+ n m)) n)))
>
> (define a (make-accum 20))
> (a) ⇒ 20
> (a 10) ⇒ 30
> (a) ⇒ 30
>
> “case-lambda” specifies a procedure that can take arguments in as many
> different shapes as there are clauses. Here there are two clauses: one
> for the case where no arguments are provided and another where one
> argument (bound to “m”) is provided.
>
> Furthermore, you can see here that this is a higher order procedure, as
> “make-accum” takes an argument and returns a procedure (the
> case-lambda).

Oh wow! Man am I going to have fun with case-lambda! Thanks for
pointing that out!

Toggle quote (10 lines)
>
> Another example, also from the manual, is this:
>
> (lambda* (start #:optional (end (+ 10 start)))
> (do ((i start (1+ i)))
> ((> i end))
> (display i)))
>
> This procedure takes one or two arguments.

Thanks again for explaining this to me. I've learned a lot from this
interaction!

Do you think there are some warnings in guile that should be escalated
to errors that refuse to compile? How about this example? (later on I
changed my mind and realized this example doesn't really prove my point).

#+BEGIN_SRC scheme
(use-modules (guix-records))

(define-record-type* <lunch-time>
lunch-time make-lunch-time
lunch-time?
(period lunch-time-period (default "10 min")))

(define-record-type* <lunch>
lunch make-lunch
lunch?
(food lunch-food (default "pizza"))
(duration lunch-duration
;; here time should by lunch-time instead
(default (time (period "30 min")))))

(lunch)
#+END_SRC

When I pasted this into the REPL, the result is:

;;; <stdin>:26:0: warning: possibly unbound variable `time'
;;; <stdin>:26:0: warning: possibly unbound variable `period'
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Unbound variable: time

Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.

This appears to me to be a case where, there is NO way to properly make
a lunch record without creating a run time error.

Whoops. Actually there is a proper way to make a lunch record:

#+BEGIN_SRC scheme
(make-lunch "sandwich" "one hour")
#+END_SRC

I suppose the moral of the story is that scheme is so expressive and
flexible that there are ways of creating programs that can fail in weird
ways. And it's really hard if not impossible to catch all possible
runtime errors at compile time. This is because scheme values are only
know at run-time AND NOT compile time.

I was actually listening to a scheme talk recently about typed racket.
The gentleman giving the talk explained that dynamic typing used to be
all the rage, but there seems to be some people advocating for static
typing because the compiler eliminates many trivial bugs. However, some
elegant and correct dynamic programs would be eliminated by the
compiler as causing errors.

typed scheme | untyped scheme
----------------------------------------------------------------
- potentially faster | - potentially slower
- a procedure's inputs | - more expressive
and outputs are obvious | - more concise
- catches trivial errors |
- helps refactoring |
- eliminates "correct"
dynamic programs

I suppose that what I am wanting (forcing the compiler to eliminate
trivial bugs) may only be possible in a typed scheme. Is that correct?

What are your thoughts? Typed or un-typed scheme?

Thanks,

Joshua

--
Joshua Branson (joshuaBPMan in #guix)
Sent from Emacs and Gnus
"You can have whatever you want, as long as you help
enough other people get what they want." - Zig Ziglar
T
(address . bug-guile@gnu.org)
20210122162918.GA27617@tuxteam.de
On Fri, Jan 22, 2021 at 09:47:24AM -0500, jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language wrote:
Toggle quote (41 lines)
> Ricardo Wurmus <rekado@elephly.net> writes:
>
> > Hi Joshua,
> >
> >> When I look at
> >>
> >> #+BEGIN_SRC scheme
> >> (define (thunk)
> >> (lambda (x)
> >> x))
> >> #+END_SRC
> >
> > […]
> >
> >> My thought is, this is clearly a mistake. This person needs to change
> >> the above code.
> >
> > How is this clearly a mistake? The definition of “thunk” above is
> > perfectly fine and also common.
>
> Thanks again for responding. I'm still learning scheme, and it's cool
> that this email chain has helped clarify some things. :)
>
> Ahh. I see now that the proper way to call thunk is to do this:
> ((thunk) "the")
> $1 = "the"
>
> I had assumed that every time one called thunk, it would result in a
> runtime error. I did not realize that there was a way to properly call
> thunk. Wow. Scheme is truly impressive.
>
> Interestingly, I had wrongly assumed that
>
> #+BEGIN_SRC scheme
> (thunk "test\n") ;; I assumed program execution would stop here
> (display "Hello World\n")
> #+END_SRC
>
> program execution would stop at (thunk "test\n"). But it actually
> caries on with execution of the program:

What happens is an "exception". It can be handled (then it's up
to the exception handler to end the program or do something else).

Toggle quote (9 lines)
> #+BEGIN_SRC scheme
> <stdin>:5:0: warning: possibly wrong number of arguments to `thunk'
> ice-9/boot-9.scm:1669:16: In procedure raise-exception:
> Wrong number of arguments to #<procedure thunk ()>
>
> Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
> Hello World
> #+END_SRC

This is the REPL's exception handler talking to you. It's there to help
you debug the problem.

Were it a standalone program, it would have terminated right away.

(note that I'm not the most appropriate person to explain such things,
I can barely wrap my head around them :-)

Cheers


- tomás
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iEYEARECAAYFAmAK/V4ACgkQBcgs9XrR2kbIxACePKWcPtVfmvitEMIdQnhQOmWI
RoQAniSIEmjI/j6/5h+0xP1fHCe+56SW
=JAdy
-----END PGP SIGNATURE-----


R
R
Ricardo Wurmus wrote on 23 Jan 2021 16:02
(name . Joshua Branson)(address . jbranso@dismail.de)
87v9bnk87q.fsf@mdc-berlin.de
Hi Joshua,

Toggle quote (19 lines)
> Interestingly, I had wrongly assumed that
>
> #+BEGIN_SRC scheme
> (thunk "test\n") ;; I assumed program execution would stop here
> (display "Hello World\n")
> #+END_SRC
>
> program execution would stop at (thunk "test\n"). But it actually
> caries on with execution of the program:
>
> #+BEGIN_SRC scheme
> <stdin>:5:0: warning: possibly wrong number of arguments to `thunk'
> ice-9/boot-9.scm:1669:16: In procedure raise-exception:
> Wrong number of arguments to #<procedure thunk ()>
>
> Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
> Hello World
> #+END_SRC

It doesn’t actually carry on. From those Org-mode blocks I cannot tell
how you’re feeding the expressions to Guile. If you’re doing this in a
file or in a REPL session manually you’ll see this:

Toggle snippet (12 lines)
scheme@(guile-user)> (define (thunk) (lambda (x) x))
scheme@(guile-user)> (thunk "test\n")
;;; <stdin>:3640:0: warning: possibly wrong number of arguments to `thunk'
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Wrong number of arguments to #<procedure thunk ()>

Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> (display "Hello World\n")
Hello World
scheme@(guile-user) [1]>

“Entering a new prompt” is what happens. Consider it a special debug
mode in which you’re given the option to inspect the current state of
the environment. Granted, you won’t see much when you type “,bt” or
“,bt #:full? 'yup”, because the error isn’t all that complicated; the
error didn’t happen in some deeply nested callee, it happened right
there when you called “thunk” with an argument.

Toggle quote (3 lines)
> For fun I also thought about how else I could write thunk. Continue
> reading at your own peril.

I feel the need to point out that the name “thunk” has a conventional
meaning, but your examples (here and before) don’t correspond to that
meaning. A thunk is a procedure of no arguments. When called it
returns a value — it does not matter whether that value is another
procedure, a record, or a primitive value.

What you seem to be calling “thunk” is really just a higher order
procedure, i.e. a procedure that returns another procedure. Note that
this may or may not be a thunk as everybody else calls it. It is a
thunk when it is a procedure that takes exactly zero arguments.

Toggle quote (6 lines)
> ;; This procedure doesn't work the way I thought it would. The way to
> ;; print a string with this procedure is to do this:
> ;; ((thunk "the") "the")
> (define (thunk x)
> (lambda (x) x))

You’re making things difficult for you here, because you’re using “x”
three times, but it means two different things dependent on where you
look at the value you bound to “x”. This is also not a thunk (neither
the procedure “thunk” nor the value it returns), so let’s rewrite this:

(define (moo x)
(peek 'moo-x x)
(lambda (x)
(peek 'lambda-x x)))

Nobody cares about the value you pass to “moo”. It is bound to the name
“x”, but nobody uses it. Then comes along a lambda and it takes an
argument that is also known as “x” inside the body of that lambda. They
are *not* the same “x”. The “x” bound by the lambda shadows the outer
“x”. You could even have yet another “x”:

(define x 100)
(peek 'top-level-x x)
(define (moo x)
(peek 'moo-x x)
(lambda (x)
(peek 'lambda-x x)))

Excess xes!

Toggle quote (6 lines)
> ;; obvious. This is equivalent to
> ;; (define (thunk x) x)
> (define thunk
> (lambda (x)
> x))

Correct. Not a thunk, though. It’s just the identity function.

Toggle quote (5 lines)
> ;; This ones nice because neither (thunk) nor (thunk "the") result in a
> ;; runtime error.
> (define* (thunk #:optional x)
> x)

This is a procedure that will return #false (when no argument is
provided) or the value of the argument it was given.

Toggle quote (4 lines)
> (define* (thunk #:optional x)
> (lambda* (#:optional x)
> x))

This is again like the “moo” example earlier. Two different values are
bound to variables that are known as “x” in their own scope, with the
later “x” shadowing the earlier “x”.

Toggle quote (3 lines)
> Are there some other really weird and convoluted ways of writing thunk
> that I'm missing? I'm guessing so.

I don’t know what you mean because your definitions above are not all
doing the same thing. A giraffe is not a convoluted variant of a lion.

Toggle quote (3 lines)
> […] Now I realize that ((thunk "the") "the")
> works.

((thunk 124) "the") has the same return value, because you’re ignoring
the first “x”.

Toggle quote (6 lines)
> I suppose the moral of the story is that scheme is so expressive and
> flexible that there are ways of creating programs that can fail in weird
> ways. And it's really hard if not impossible to catch all possible
> runtime errors at compile time. This is because scheme values are only
> know at run-time AND NOT compile time.

This is not 100% correct, but perhaps that doesn’t matter. Some types
are in fact known at compile time.

Toggle quote (22 lines)
> I was actually listening to a scheme talk recently about typed racket.
> The gentleman giving the talk explained that dynamic typing used to be
> all the rage, but there seems to be some people advocating for static
> typing because the compiler eliminates many trivial bugs. However, some
> elegant and correct dynamic programs would be eliminated by the
> compiler as causing errors.
>
> typed scheme | untyped scheme
> ----------------------------------------------------------------
> - potentially faster | - potentially slower
> - a procedure's inputs | - more expressive
> and outputs are obvious | - more concise
> - catches trivial errors |
> - helps refactoring |
> - eliminates "correct"
> dynamic programs
>
> I suppose that what I am wanting (forcing the compiler to eliminate
> trivial bugs) may only be possible in a typed scheme. Is that correct?
>
> What are your thoughts? Typed or un-typed scheme?

I learned functional programming with Haskell, which is more type than
language ;) Type systems allow you to encode certain assumptions in a
way that the compiler can check for you.

Note that “potentially slower” doesn’t mean much unless you look at
actual implementations. Types allow the compiler to perform certain
optimizations because it can trust that certain assumptions will hold at
runtime. But these optimizations would actually have to be implemented
in the compiler; you don’t get them for free just because you have type
declarations. Likewise, you can have a fast untyped Scheme and a slow
untyped Scheme dependent on the optimizations that are implemented. You
can also get fast or slow compilers…

The point about types eliminating “correct” dynamic programs is
noteworthy. Types are a constraint and dependent on how strictly they
are enforced they can make it impossible to do things that are sensible.
It is, however, possible to declare an “Any” type and write programs
that are transformations of values of the “Any” type, which would be
correctly typed; but the effect is to bypasses the type checker, which
renders it useless.

So… yeah, it’s not that clear cut.

Sometimes I do miss types in my Scheme code, especially in more complex
programs with lots of higher-order functions where it’s easy to get
lost. But most of the time I don’t care for types.

--
Ricardo
J
J
Joshua Branson wrote on 23 Jan 2021 16:07
closing the bug report...hopefully
(address . 46014@debbugs.gnu.org)
87im7nzo6q.fsf@dismail.de
close 46014
J
J
Joshua Branson wrote on 23 Jan 2021 16:17
Re: bug#46014: (define (thunk) (lambda (x) x)) should be a compile error?
(name . Ricardo Wurmus)(address . rekado@elephly.net)
87bldfznqy.fsf@dismail.de
Hey Ricardo!

Thanks for taking the time to explain that thunk is a procedure with no
arguments, and that the first x is not used in some of those procedures.

Feel free to close this bug report. I don't believe I was able to
demonstrate any examples of the compiler misbehaving.

Thanks!

--
Joshua Branson (joshuaBPMan in #guix)
Sent from Emacs and Gnus
"You can have whatever you want, as long as you help
enough other people get what they want." - Zig Ziglar
R
R
Ricardo Wurmus wrote on 23 Jan 2021 18:23
(name . Joshua Branson)(address . jbranso@dismail.de)
87sg6rk1o5.fsf@elephly.net
Joshua Branson <jbranso@dismail.de> writes:

Toggle quote (3 lines)
> Feel free to close this bug report. I don't believe I was able to
> demonstrate any examples of the compiler misbehaving.

Okay, closing!

--
Ricardo
?
Your comment

This issue is archived.

To comment on this conversation send an email to 46014@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 46014
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch