unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* On procedures within procedures
@ 2013-04-03 14:04 Mike Gran
  2013-04-03 15:11 ` Noah Lavine
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Mike Gran @ 2013-04-03 14:04 UTC (permalink / raw)
  To: Guile User

I've been looking over one of the solutions to a Guile 100 problem
(a basic version of the `ls' command) at the link below.  It is
interesting stylistically because it works very hard at minimizing the
scope of procedures.  It uses a lot of procedures within procedures,
like, for example, within a `let*' block.


https://gist.github.com/spk121/5301264


For example
(define (outer-function x)
(let*
((inner-function 
(lambda (b . M)
(let ((m (fold logior 0 M)))
(eq? m (logand b m)))))
...

Personally, I only define a procedure within a procedure if the
inner procedure is not a pure function, e.g. if its result
depends on information other than its parameters, and that
information only exists within the scope of the outer procedure.
I tend to keep most pure functions at the module level. But,
I'm not a very Schemey guy.

One could look at using lots of procedures within procedures as good
defensive programming style: hiding unnecessary functions.

Or one could look at it as being less clear because you end up with
longer "paragraphs" to read.

Do you have a personal philosophy on how much you try to minimize
the scope of procedures?  If you were writing a tutorial,
what would you say?

-Mike Gran




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 14:04 On procedures within procedures Mike Gran
@ 2013-04-03 15:11 ` Noah Lavine
  2013-04-03 15:46 ` Ian Price
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Noah Lavine @ 2013-04-03 15:11 UTC (permalink / raw)
  To: Mike Gran; +Cc: Guile User

[-- Attachment #1: Type: text/plain, Size: 2096 bytes --]

I often write things the way you do. I find that it's easier to test if I
can get at as many procedures as possible from the REPL (but maybe I'm not
very Schemey either). I also find it harder to read procedures declared in
let blocks sometimes because they make the flow of control jump around on
the page - first you have to read the body of the let block, which is lower
on the screen, and then jump back up to the functions that were defined at
the top of the block.

However, I think that inner functions make a lot of sense if you think of
each top-level procedure like its own little module. If you think like
that, then writing inner procedures means grouping related code together
and making the overall structure more clear.

Noah


On Wed, Apr 3, 2013 at 10:04 AM, Mike Gran <spk121@yahoo.com> wrote:

> I've been looking over one of the solutions to a Guile 100 problem
> (a basic version of the `ls' command) at the link below.  It is
> interesting stylistically because it works very hard at minimizing the
> scope of procedures.  It uses a lot of procedures within procedures,
> like, for example, within a `let*' block.
>
>
> https://gist.github.com/spk121/5301264
>
>
> For example
> (define (outer-function x)
> (let*
> ((inner-function
> (lambda (b . M)
> (let ((m (fold logior 0 M)))
> (eq? m (logand b m)))))
> ...
>
> Personally, I only define a procedure within a procedure if the
> inner procedure is not a pure function, e.g. if its result
> depends on information other than its parameters, and that
> information only exists within the scope of the outer procedure.
> I tend to keep most pure functions at the module level. But,
> I'm not a very Schemey guy.
>
> One could look at using lots of procedures within procedures as good
> defensive programming style: hiding unnecessary functions.
>
> Or one could look at it as being less clear because you end up with
> longer "paragraphs" to read.
>
> Do you have a personal philosophy on how much you try to minimize
> the scope of procedures?  If you were writing a tutorial,
> what would you say?
>
> -Mike Gran
>
>
>

[-- Attachment #2: Type: text/html, Size: 2715 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 14:04 On procedures within procedures Mike Gran
  2013-04-03 15:11 ` Noah Lavine
@ 2013-04-03 15:46 ` Ian Price
  2013-04-03 20:13   ` Ludovic Courtès
  2013-04-03 19:33 ` Thien-Thi Nguyen
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Ian Price @ 2013-04-03 15:46 UTC (permalink / raw)
  To: Mike Gran; +Cc: Guile User

Mike Gran <spk121@yahoo.com> writes:

> I've been looking over one of the solutions to a Guile 100 problem
> (a basic version of the `ls' command) at the link below.  It is
> interesting stylistically because it works very hard at minimizing the
> scope of procedures.  It uses a lot of procedures within procedures,
> like, for example, within a `let*' block.
I think I first heard of people doing this in one of the "letrec
reloaded" papers. It confused me then; it confuses me now :).

> Personally, I only define a procedure within a procedure if the
> inner procedure is not a pure function, e.g. if its result
> depends on information other than its parameters, and that
> information only exists within the scope of the outer procedure.
> I tend to keep most pure functions at the module level. But,
> I'm not a very Schemey guy.
Well, you can get around this by passing in the free variables if you
really want to put everything at the top level (some functional
compilers do this, and it is called lambda-lifting).

> One could look at using lots of procedures within procedures as good
> defensive programming style: hiding unnecessary functions.
>
> Or one could look at it as being less clear because you end up with
> longer "paragraphs" to read.
This is how I'd view it: it's cute, but unless you are using a Scheme
implementation without a module system (i.e. a bad one), I don't think
it's going to do you much good in the long run.

> Do you have a personal philosophy on how much you try to minimize
> the scope of procedures?  If you were writing a tutorial,
> what would you say?
For me it boils down to the question of "will I use this in another
procedure"? If no, keep it as an inner define; if yes, put it in the top
level.

Obviously, I'm not a clairvoyant, so this is usually a guess, but
experience gives you a "feel" for it, and you can revise later.

_Sometimes_ I do it because I know Guile can inline it if it is an inner
define (naughty, naughty).

-- 
Ian Price -- shift-reset.com

"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 14:04 On procedures within procedures Mike Gran
  2013-04-03 15:11 ` Noah Lavine
  2013-04-03 15:46 ` Ian Price
@ 2013-04-03 19:33 ` Thien-Thi Nguyen
  2013-04-04 13:26 ` Nikita Karetnikov
  2013-04-05  6:12 ` Mark H Weaver
  4 siblings, 0 replies; 7+ messages in thread
From: Thien-Thi Nguyen @ 2013-04-03 19:33 UTC (permalink / raw)
  To: Guile User

[-- Attachment #1: Type: text/plain, Size: 1907 bytes --]

() Mike Gran <spk121@yahoo.com>
() Wed, 3 Apr 2013 07:04:43 -0700 (PDT)

   Do you have a personal philosophy on how much you try to
   minimize the scope of procedures?

I figure out the data structures first, then use procedures w/o
overmuch regard to inner/outer.  However, i...

   If you were writing a tutorial, what would you say?

...do try to keep to a minimum the number of arguments that are
passed from a procedure's caller to procedures it calls.

For example, in SGF Utils 0.6 src/render.scm, the procedure
‘make-screen-1’ is almost 700 lines long w/ 90 internal
procedures, the deepest being 6 levels (at least, depending on
what you count, precisely) from top.  This particular proc, i.e.,
‘make-screen-1 ray! one! poly-vecs two-side normal’:

 (define (two-sides actually)
 
   (define (normal direction)
     (make-polar radius (+ a (* (/ PI 2) direction))))
 
   (let-values (((mx my) (actually (normal -1)))
                ((px py) (actually (normal  1))))
     (values mx my px py)))

is not actually as tight as it could be:

 (define (two-sides actually)
 
   (define (normal direction)
     (actually (make-polar radius (+ a (* (/ PI 2) direction)))))
 
   (let-values (((mx my) (normal -1))
                ((px py) (normal  1)))
     (values mx my px py)))

but hey, that's a code enhancement opportunity to mull over while
posting, what luck!  The point is, the usual alternative to this
structuring would be to define a "graphics and board context, both
fundamental and elaborated" object to pass around.  Shudder.  I'd
rather have that monster out in the open and fixed (lexically),
than roaming hidden behind getters / setters and All That Rot (er,
entropy :-D).  (It helps, too, to live in Emacs, froth froth.)

OK, time to calm down.  Scheme is dangerously invigorating...

-- 
Thien-Thi Nguyen
GPG key: 4C807502

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 15:46 ` Ian Price
@ 2013-04-03 20:13   ` Ludovic Courtès
  0 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2013-04-03 20:13 UTC (permalink / raw)
  To: guile-user

Ian Price <ianprice90@googlemail.com> skribis:

> Sometimes_ I do it because I know Guile can inline it if it is an inner
> define (naughty, naughty).

Same, and also for clarity.

Ludo’.




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 14:04 On procedures within procedures Mike Gran
                   ` (2 preceding siblings ...)
  2013-04-03 19:33 ` Thien-Thi Nguyen
@ 2013-04-04 13:26 ` Nikita Karetnikov
  2013-04-05  6:12 ` Mark H Weaver
  4 siblings, 0 replies; 7+ messages in thread
From: Nikita Karetnikov @ 2013-04-04 13:26 UTC (permalink / raw)
  To: Mike Gran; +Cc: Guile User

[-- Attachment #1: Type: text/plain, Size: 321 bytes --]

> One could look at using lots of procedures within procedures as good
> defensive programming style: hiding unnecessary functions.

Here is a related part of SICP [1] which describes this approach.

[1] https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-10.html#%_sec_1.1.8 (Internal definitions and block structure)

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: On procedures within procedures
  2013-04-03 14:04 On procedures within procedures Mike Gran
                   ` (3 preceding siblings ...)
  2013-04-04 13:26 ` Nikita Karetnikov
@ 2013-04-05  6:12 ` Mark H Weaver
  4 siblings, 0 replies; 7+ messages in thread
From: Mark H Weaver @ 2013-04-05  6:12 UTC (permalink / raw)
  To: Mike Gran; +Cc: Guile User

Mike Gran <spk121@yahoo.com> writes:
> I've been looking over one of the solutions to a Guile 100 problem
> (a basic version of the `ls' command) at the link below.  It is
> interesting stylistically because it works very hard at minimizing the
> scope of procedures.  It uses a lot of procedures within procedures,
> like, for example, within a `let*' block.
>
> https://gist.github.com/spk121/5301264

Apart from stylistic issues, I spotted a few errors in the code.

  (digits (lambda (n) (if (eq? n 0) 0 (1+ (ceiling (log10 n))))))

This has two problems.  First, the use of inexact arithmetic (via log10)
means that 'digits' is not guaranteed to compute the correct answer.
That's not good.  Also, for purposes of this formatting task, (digits 0)
should be 1, not 0.

Second, 'eq?' is not guaranteed to work on numbers.  '=' should be used
here, or 'zero?'.  'eqv?' could be safely used, but it has a different
meaning than '='.  'eqv?' tests "operational equivalence", whereas '='
tests "numerical equality".  Numerical equality is the right thing here.

There are other places where 'eq?' is used inappropriately:

   (bits-set?
    (lambda (bits . masks)
      (let ((mask (fold logior 0 masks)))
        (eq? mask (logand bits mask)))))

The code also uses 'eq?' to compare characters, e.g.:

  (define (string-starts-with? s c) (eq? c (string-ref s 0)))

  (if (eq? type #\l) ...)

'eq?' is not guaranteed to work on characters either.  To compare
characters, use 'char=?'.  'eqv?' could also work, but IMO it's cleaner
to use 'char=?' when you know that both arguments are characters.

     Mark



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2013-04-05  6:12 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-03 14:04 On procedures within procedures Mike Gran
2013-04-03 15:11 ` Noah Lavine
2013-04-03 15:46 ` Ian Price
2013-04-03 20:13   ` Ludovic Courtès
2013-04-03 19:33 ` Thien-Thi Nguyen
2013-04-04 13:26 ` Nikita Karetnikov
2013-04-05  6:12 ` Mark H Weaver

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