23

In Python, I can do something like this:

t = (1, 2)
a, b = t

...and a will be 1 and b will be 2. Suppose I have a list '(1 2) in Scheme. Is there any way to do something similar with let? If it makes a difference, I'm using Racket.

YasirA
  • 9,531
  • 2
  • 40
  • 61
Jason Baker
  • 192,085
  • 135
  • 376
  • 510

6 Answers6

29

In racket you can use match,

(define t (list 1 2))
(match [(list a b) (+ a b)])

and related things like match-define:

(match-define (list a b) (list 1 2))

and match-let

(match-let ([(list a b) t]) (+ a b))

That works for lists, vectors, structs, etc etc. For multiple values, you'd use define-values:

(define (t) (values 1 2))
(define-values (a b) (t))

or let-values. But note that I can't define t as a "tuple" since multiple values are not first class values in (most) scheme implementations.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
  • This works, but I was looking for something that used `let`, and this `defines` it. I suppose I could write a macro that splices such a definition into `local` though. – Jason Baker Nov 20 '10 at 00:28
  • 2
    Well, there's `match-let` (updated with an example), but a simple `match` can do too. (Your question made it look like you *wanted* definitions.) Also, you can always use the definitions in a local scope. – Eli Barzilay Nov 20 '10 at 03:14
  • The first example doesn't run (and it doesn't make sense in my head) – cosarara97 Dec 09 '19 at 23:16
  • You should be using the `racket` language. Click on the links I provided to see the reference documentation. – Eli Barzilay Dec 15 '19 at 19:14
12

A bare-bones idiom is to use apply with lambda where you'd use let, like:

(define t '(1 2))
(apply (lambda (a b)
          ;; code that would go inside let
        )
        t)

The advantage is that it works on any implementation. Of course this can only be used on simple cases, but sometimes that's all you need.

Watcom
  • 2,359
  • 1
  • 17
  • 15
8

The general term for what you're looking for (at least in Lisp-world) is destructuring and a macro that implements it is known as destructuring-bind. In Common Lisp, it works like this:

(destructuring-bind (a b c) '(1 2 3)
  (list a b c)) ;; (1 2 3)

it also works for multiple "levels" of nesting:

(destructuring-bind (a (b c) d) '(1 (2 3) 4)
  (list a b c d)) ;; (1 2 3 4)

It looks like there's a nice implementation of destructuring-bind as a scheme macro.

Chris
  • 3,000
  • 26
  • 43
4

I think this is what you are looking for:

Look at let-values or let+.

icyrock.com
  • 27,952
  • 4
  • 66
  • 85
  • Thanks for posting this! The only thing is that `let-values` doesn't do *quite* what I wanted it to do, and I can't seem to get the library that is required to use `let+` working. That said, this "scheme for Python programmers" website will certainly come in handy. – Jason Baker Nov 20 '10 at 00:30
  • Well, at least you have a cool new site to dig through if you run into other problems. Take a look at it, hope you will find how to set up your environment for `let+`. Cheers. – icyrock.com Nov 20 '10 at 00:35
2

This works in Racket if you don't want to bring in the match dependency:

From a list:

(let-values ([(a b c) (apply values '(1 2 3))])
  (+ a b c))

Or directly from a values expression:

(let-values ([(a b c) (values 1 2 3)])
  (+ a b c))
Jason Stewart
  • 374
  • 4
  • 7
2

Here is a simple destructuring-bind macro for schemes with case-lambda (such as Racket or Chez Scheme):

(define-syntax bind
   (syntax-rules ()
      ((_ arg pat def body)
         (apply
            (case-lambda
               [pat body]
               [x def] )
            arg ))))

Here is the example that motivated me to write this macro. Putting the default before the body makes for readable code:

(define (permutations l)
   ;
   (define (psub j k y)
      ;
      (define (join a b)
         (bind a (ah . at) b
            (join at (cons ah b)) ))
      ;
      (define (prec a b z)
         (bind b (bh . bt) z
            (prec (cons bh a) bt
               (psub (cons bh j) (join a bt) z) )))
      ;
      (if (null? k)
         (cons (reverse j) y)
         (prec (list) k y) ))
   ;
   (psub (list) (reverse l) (list)) )

Here are benchmarks for computing permutations of length 9, on various schemes:

0m0.211s Chez Scheme
0m0.273s Bigloo
0m0.403s Chicken
0m0.598s Racket

The translation to GHC Haskell is 5x faster than Chez Scheme. Guile is much slower than any of these schemes.

Aside from the ease of leveraging the existing case-lambda code, I like how this macro accepts exactly the same syntax as function definition argument lists. I love the simplicity of scheme. I'm old enough to remember programming Fortran on punched cards, where the allowed syntax varied wildly with context. Scheme is supposed to be better than that. The impulse is overwhelming to guild the lily on macros like this. If you can't justify changing the syntax for function definitions too, then don't change that syntax here either. Having an orthogonal grammar is important.

Syzygies
  • 21
  • 3