HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

in-nest-sequence: sequence generator in Racket

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
nestracketgeneratorsequence

Problem

in-nest-sequence is a sequence generator that takes a function and an initial value, and the return value of invoking the function on the current value is used as the subsequent value. For example, (in-nest-sequence add1 0) returns the sequence (0 1 2 3 4 ...).

Recently, soegaard challenged me to write a define-sequence-syntax version of in-nest-sequence, that is, a macro-based version. I decided to give it a try:

`#lang racket
(require (for-syntax unstable/syntax))
(provide (rename-out [*in-nest-sequence in-nest-sequence]))

(define in-nest-sequence
(case-lambda
[(func init)
(make-do-sequence
(thunk (values identity func init #f #f #f)))]
[(func . inits)
(make-do-sequence
(thunk (values (curry apply values)
(lambda (args)
(call-with-values (thunk (apply func args)) list))
inits #f #f #f)))]))

(define-sequence-syntax *in-nest-sequence
(lambda () #'in-nest-sequence)
(lambda (stx)
(syntax-case stx ()
[[(x ...) (_ func init ...)]
(unless (= (syntax-length #'(x ...)) (syntax-length #'(init ...)))
(raise-syntax-error 'in-nest-sequence
(format "~a values required" (syntax-length #'(x ...)))
stx #'(init ...)))
(with-syntax ([for-arity (syntax-length #'(init ...))]
[(value ...) (generate-temporaries #'(init ...))]
[(y ...) (generate-temporaries #'(init ...))])
#'[(x ...) (:do-in ([(f) func])
(unless (procedure-arity-includes? f for-arity)
(raise-arity-error f (procedure-arity f) init ...))
([value init] ...)
#t
([(x ...) (values value ...)]
[(y ...) (f value ...)])
#t
#t
(

Solution

For cases where the the user follows the syntax and the function returns and receives the correct number of values, this works fine.

It is a good principle to catch errors as early as possible.

Consider this example:

(in-iterate add1)

Here the user supplies a function add1 that has arity 1, but supplies no arguments. This will eventually lead to an "result arity mismatch;" error, when add1. This error can be caught earlier in using procedure-arity to check that enough init values are supplied. The error message presented to the user can be "expected 1 initial value, but received 0".

In the *in-iterate consider: [(x ...) (_ func init ...)] .
Here the number of bound identifiers (the xs) must be the same as the number of init expressions. If they are not, a raise-syntax-error with stx as the source of the error can be used.

An example that should be a syntax-error (and not a runtime error):

(define (add1/2 x y) (values (+ x 1) (+ y 2)))
(for/list ([n     10]
           [(x y) (in-iterate add1/2 0)])
  (list x y))


The name of the construct is a bit off. In in-list and in-sequence we have the pattern in-noun, so that suggest in-iteration might be better. However iteration is a bit vague. Maybe one in-nested-sequence or in-nest-sequence based on the name used in Mathematica? http://reference.wolfram.com/language/ref/NestList.html

Code Snippets

(define (add1/2 x y) (values (+ x 1) (+ y 2)))
(for/list ([n     10]
           [(x y) (in-iterate add1/2 0)])
  (list x y))

Context

StackExchange Code Review Q#106806, answer score: 2

Revisions (0)

No revisions yet.