unofficial mirror of bug-guile@gnu.org 
 help / color / mirror / Atom feed
From: Ricardo Wurmus <rekado@elephly.net>
To: Joshua Branson <jbranso@dismail.de>
Cc: 46014@debbugs.gnu.org
Subject: bug#46014: (define (thunk) (lambda (x) x)) should be a compile error?
Date: Sat, 23 Jan 2021 16:02:01 +0100	[thread overview]
Message-ID: <87v9bnk87q.fsf@mdc-berlin.de> (raw)
In-Reply-To: <87o8hhyqo3.fsf@dismail.de>


Hi Joshua,

> 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:

--8<---------------cut here---------------start------------->8---
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]> 
--8<---------------cut here---------------end--------------->8---

“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.

> 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.

> ;; 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!

> ;; 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.

> ;; 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.

> (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”.

> 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.

> […] Now I realize that ((thunk "the") "the")
> works.

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

> 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.

> 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





  parent reply	other threads:[~2021-01-23 15:02 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-21  2:04 bug#46014: (define (thunk) (lambda (x) x)) should be a compile error? jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language
2021-01-21 13:56 ` Ricardo Wurmus
2021-01-21 18:16   ` Stefan Israelsson Tampe
2021-01-21 20:11     ` jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language
2021-01-21 20:09   ` jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language
2021-01-21 22:27     ` Ricardo Wurmus
2021-01-22 14:47       ` jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language
2021-01-22 16:29         ` tomas
2021-01-23 15:02         ` Ricardo Wurmus [this message]
2021-01-23 15:17           ` jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language
2021-01-23 17:23             ` Ricardo Wurmus
2021-01-23 15:07 ` bug#46014: closing the bug report...hopefully jbranso--- via Bug reports for GUILE, GNU's Ubiquitous Extension Language

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87v9bnk87q.fsf@mdc-berlin.de \
    --to=rekado@elephly.net \
    --cc=46014@debbugs.gnu.org \
    --cc=jbranso@dismail.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).