2015-08-02 19:12 GMT+02:00 Konstantin Weitz <konstantin.we...@gmail.com>:

> I'm trying to write a distributed racket program using [places][0]. From
> my current understanding, this means that I have to send data to each place
> over a channel using the `place-channel-put` function, which can send any
> value accepted by `place-message-allowed?`.
>
> I would like to send closures over a channel, unfortunately, closures are
> not accepted by `place-message-allowed?`. Is there any way to serialize a
> closure into a value accepted by a channel, and then deserialize it on the
> other side of the channel?
>

How many lambda expressions are we talking about?
If the number is limited you can defined custom closures.

Whether or not the following is practical I will leave up to you
to decide :-)


Consider this program.

    (define x 3)
    (define f (lambda (y) (+ x y)))
    (f 4)

Annotate the free variables of the lambda-expression.

    (define x 3)
    (define f (lambda (y) (+ x y)))  ; x is free
    (f 4)

Define an explicit closure struct and a helper to access free variables.

    (struct closure (code free) #:prefab)

The code field will refer to a closed (no free variables) version of f and
free is a vector of the values of the free variables at the
time the closure was created.

The idea is that we want to replace (lambda (y) (+ x y)) with (closure
f_closed (vector x)).

A little helper to access the free variables:

    (define (free-ref cl i)
      (vector-ref (closure-free cl) i))

Now define a closed (no free variables) version of f:

    (define f_closed
      (lambda (cl y)
        (let ((x (free-ref cl 0)))
          (+ x y))))

We can now rewrite the lambda expression into a closure allocation.

The full program is now:

    (struct closure (code free) #:prefab)
    (define (free-ref cl i)  (vector-ref (closure-free cl) i))

    (define f_closed
      (lambda (cl y)
        (let ((x (free-ref cl 0)))
          (+ x y))))

    (define x 3)
    (define f (closure f_closed (vector x)))
    (f 4)

Note that the structure is a prefab structure in order to be a legal
message.

At this point there is a complication:

    * f_closed is not an allowed message
    * (f 4) fails since the closure structure is not applicable

The first point is solved by sending the name of f_closed as a symbol.
One could use eval to change it back to a function, but a hash table will
do:

    (define closed-functions-ht
      (make-hasheq (list (cons 'f_closed f_closed))))

    (define (get-closed-function name)
      (hash-ref closed-functions-ht name
                (λ _ (error 'app "no closed function of that name ~a"
name))))

The other can unfortunately not be solved by turning the structure
into an applicable struct, since prefab structures can't have properties.
Therefore we need to introduce a construct app that knows how to invoke
closures.

    (define-syntax-rule (app cl arg ...)
      ((get-closed-function (closure-code cl)) cl arg ...))

The entire program is now:

    #lang racket
    (struct closure (code free) #:prefab)
    (define (free-ref cl i)  (vector-ref (closure-free cl) i))

    (define f_closed
      (lambda (cl y)
        (let ((x (free-ref cl 0)))
          (+ x y))))

    (define closed-functions-ht
      (make-hasheq (list (cons 'f_closed f_closed))))

    (define (get-closed-function name)
      (hash-ref closed-functions-ht name
                (λ _ (error 'app "no closed function of that name ~a"
name))))

    (define-syntax-rule (app cl arg ...)
      ((get-closed-function (closure-code cl)) cl arg ...))

    (define x 3)
    (define f (closure 'f_closed (vector x)))
    (app f 4)

The value of f is:

    > f
    '#s(closure f_closed #(3))

and that value is an allowed message.

The standard higher operations such as map and apply do not know about the
special calling convention of f, so (map f '(1 2 3)) will not work.
It is no big deal though, since wrapping f in a lambda works:

    > (map (λ (x) (app f x))
           '(1 2 3))
    '(4 5 6)

To make this solution practical one could introduce a macro Lambda
that turns (Lambda (x) (y) (+ x y)) into (closure 'closed1 (vector x)))
and at the same time lifts

    (define closed1
      (lambda (cl y)
        (let ((x (free-ref cl 0)))
          (+ x y))))

to the top of the program.

An ambitious solution would also use free-vars from syntax/free-vars
to determine the free variables automatically.

/Jens Axel

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to