Jörgen Brandt

Racket Places Tutorial

A Racket place is an independent process in Racket. Places interact by sending messages.

Besides places Racket provides a few other similar concepts:

What are the differences between places, threads, and futures? When would you use the one or the other?

Hello world

We can make a place say hello. Enter the following in the definitions window of DrRacket, save the file and start interactions. Then call (say-hello)

#lang racket/base
(require racket/place)

(define (say-hello)
  (define p (place _ (displayln "hello world")))
  (place-wait p))

There is already a lot of things going on.

  • couldn’t we create a place in the interactive window?
  • couldn’t we start the place in the top level of the module?
  • couldn’t we call (say-hello) already in the module?
  • what happens if you do not save your editor?

From now on, we use dynamic-place instead of place to create new places.

Basic concepts

Racket places talk about three things:

  • place: an independent instance of a Racket VM
  • channel: a handle to send and receive messages; they usually come in pairs
  • message: a Racket value we can pass around between places.

Place life cycle

dynamic-place

Places can be started with dynamic-place. Create a module in place-module.rkt.

#lang racket/base
(provide main)
(require racket/place)

(define (main c)
  (displayln "hello world"))

Then, in the interactive window, start the place by entering

> (dynamic-place "place-module.rkt" 'main)

place-wait

You can synchronize with a place with place-wait.

> (define p (dynamic-place "place-module.rkt" 'main)
> (place-wait p)

Sending and Receiving Messages

Let’s make a server that receives a message and prints it on the screen. For receiving we use place-channel-get. Sending uses place-channel-put.

#lang racket/base

(provide main)
(require racket/place)

(define (main c)
  
  (define m
    (place-channel-get c))

  (place-channel-put c m)
  
  (main c))

> (define p (dynamic-place "test.rkt" 'main))
> (place-channel-put p "hello world")
> (place-channel-get p)

Creating Channels

You get a pair of channel-endpoints by using place-channel.

(define-values (a b) (make-channel))

Channel endpoints can be sent to places.

Trouble Shooting

Here, we talk about everything that can go wrong.

Error Jeopardy

place: can only be used in a module

You have tried to create a place in the interactions window of DrRacket. But places can be started only in the definitions window.

place: the enclosing module's resolved name is not a path or predefined

You need to save your definitions window to disk.

syntax-local-lift-provide: not expanding in a module run-time body

You have tried to define a serializable function, a serial-lambda, in the interactions window of DrRacket. But serializable functions can be defined only in the definitions window.

place-channel-put: value not allowed in a message

You have tried to send a value over a channel that is not message-allowed. Racket forbids to send values like functions or opaque structures. You can find out if a value is message-allowed by testing it with place-message-allowed?.

Message Passing and Serializability

While for many Racket values it is obvious whether or why they can be sent over a channel guessing what values are allowed as messages can be difficult. Surprisingly, neither does serializability imply message-allowedness nor the other way around.

For example, apparently the exact integer 5 is both message-allowed and serializable. In contrast, the function (lambda (x) x) is neither message-allowed nor serializable.

However, some values are message-allowed but not serializable. For example place identifiers are:

#lang racket/base
(require racket/place)
(require racket/serialize)

(define p
  (place c (displayln "hello world")))

(serializable? p)          ; #f
(place-channel-allowed? p) ; #t

Also, some values are serializable but not message-allowed. For example serial-lambdas are:

#lang racket/base
(require web-server/lang/serial-lambda
         racket/serialize
         racket/place)

(define f
  (serial-lambda (x) x))

(serializable? f)          ; #t
(place-message-allowed? f) ; #f

So don’t forget to serialize your serializable functions.

Message Passing and Structs

Structs must be #:prefab to send them over a channel. Note that #:transparent structs are false friends being neither message-allowed nor serializable.

Sending and Receiving Functions

Racket forbids to send functions over place channels. To circumvent this problem we can use the serializable functions provided by web-server/lang/serial-lambda.

#lang racket/base
(require racket/serialize)
(require racket/place)
(require web-server/lang/serial-lambda)

(define c
  (place
   c

   (define f
     (deserialize (place-channel-get c)))

   (f)))

(define f
  (serial-lambda () (displayln "hello world")))

(place-channel-put c (serialize f))