IRacket:   Racket Kernel for Jupyter
1 Installing the IRacket Jupyter Kernel
1.1 IRacket Installation API
install-iracket!
2 IRacket and Languages
2.1 The Top Level (REPL) Is Hopeless
2.2 #lang iracket/  lang
8.12

IRacket: Racket Kernel for Jupyter🔗ℹ

Ryan Culpepper <ryanc@racket-lang.org>

This library provides a Racket kernel for Jupyter, enabling interactive notebook-style programming with Racket.

1 Installing the IRacket Jupyter Kernel🔗ℹ

After installing the iracket package, you must register the IRacket kernel with Jupyter. Kernel registration can be done either through the raco iracket install command or by using the iracket/install module.

If racket and jupyter are both in your executable search path, then you can register the kernel by either of the following:

If the jupyter command is not in your executable search path, you must tell the installer the absolute path to the jupyter executable using the --jupyter-exe flag or #:jupyter-exe keyword argument. The installer runs jupyter --data-dir to find the directory where it should install the IRacket kernel.

If the racket command is not in your executable search path, or if you want to register a kernel that uses a specific version of Racket, then you must tell the installer the path to the executable to use. The executable must support the same command-line interface that the racket executable supports, but it does not have to be named racketfor example, a racketcgc or racketcs executable would also work. See the output of raco iracket install --help or the documentation for install-iracket! for details.

Note that if you register the kernel with a non-version-specific Racket command (the default) and then change that command to run a different version of Racket (for example, by changing your PATH or by installing a new version of Racket over the old one), then Jupyter will try to use the new version of Racket to run the IRacket kernel. If the iracket package is not installed in the new version, this typically results in an error like “collection not found for module path: (lib "iracket/iracket")”. The same error will occur if you try to run the kernel after removing the iracket package.

Changed in version 1.1: Added raco iracket command.

1.1 IRacket Installation API🔗ℹ

 (require iracket/install) package: iracket

procedure

(install-iracket! [#:jupyter-exe jupyter-exe    
  #:racket-exe racket-exe])  void?
  jupyter-exe : (or/c (and/c path-string? complete-path?) #f)
   = #f
  racket-exe : (or/c path-string? 'auto 'this-version) = #f
Registers the IRacket kernel in the location reported by jupyter-exe (with the --data-dir flag). If racket-exe is a path or string, then it is included in the kernel registration as the command Jupyter should use to run the kernel. If racket-exe is #f, then the registration uses the generic racket command, but only if it is included in the current executable search path; otherwise, an error is raised. If racket-exe is 'this-version, then the absolute path to the currently running version of Racket is used.

Added in version 1.1 of package iracket.

2 IRacket and Languages🔗ℹ

The IRacket kernel does not support Racket’s #lang syntax for selecting languages, for the same reason that syntax doesn’t work at the Racket REPL. That is, #lang is a syntax for whole modules, whereas both the REPL and Jupyter work with top-level forms and generally receive them one at a time. See also The Top Level (REPL) Is Hopeless.

Instead of general #lang support, IRacket recognizes #lang iracket/lang as a special declaration for adjusting the notebook’s language.

#lang iracket/lang #:require lang-mod maybe-reader
 
maybe-reader = 
  | #:reader reader-mod

Creates a new empty namespace, populates it by requiring lang-mod (a module path), and installs it as the kernel’s current namespace, used for evaluation. The namespace shares the module instances of the kernel’s original namespace, but it does not include previous top-level definitions.

If reader-mod is given, the kernel’s reader is set to the read-syntax export of reader-mod (a module path); otherwise the kernel’s reader is set to Racket’s read-syntax.

Warning: If reader-mod is given, its read-syntax export must be suitable for reading top-level forms. For example, scribble/reader is suitable, but at-exp/lang/reader is not suitable, because it provides a whole-module meta-reader.

If a cell contains #lang iracket/lang, it must be the first thing in the cell; no other forms, comments, or even whitespace can appear before it. The declaration does not have to appear in the first cell in the notebook; in fact, multiple cells may contain language declarations. The declaration takes effect when it is evaluated, and it affects the rest of the current cell and subsequent evaluations, until the kernel is restarted or until the next #lang iracket/lang declaration is evaluated.

For example, the following declaration sets the initial environment to the exports of racket/base and sets the reader to Scribble’s reader:

#lang iracket/lang #:require racket/base #:reader scribble/reader

Added in version 1.2.

2.1 The Top Level (REPL) Is Hopeless🔗ℹ

Due to the combination of Racket’s macro system, its recursive top-level environment, and the fact that the REPL receives and processes forms one at a time, the Racket REPL occasionally produces unexpected behavior. These problems are known in the Racket community as “the top level is hopeless”. The same problems occur in Jupyter notebooks.

For example, consider the following program:

; range : Real Real -> Real
; Compute the size of the given interval.
(define (range x y)
  (cond [(<= x y) (- y x)]
        [else ; swap to normalize, try again
         (range y x)]))
(range 5 10)
(range 10 5)

When placed inside a #lang racket module, this code prints 5 twice. But when run at the REPL (or in a racket/load module), the same code prints 5 and then '(5 6 7 8 9)!

The problem is that when the REPL receives the definition of range, it compiles the definition in an environment where range is still bound to range from racket (which re-provides the exports of racket/list). So the “recursive” call to range gets compiled as a call to racket/list’s range, not to the top-level range function that has not yet been defined.

Racket’s module system mainly avoids such problems by detecting defined names before expanding the right-hand sides of definitions.

To avoid this problem, avoid redefining names.

(An alternative is to put (define-syntaxes (range) (values)) before the definition above. This form of define-syntaxes is only allowed at the top level, and it changes range to resolve as a binding in the top-level environment without giving it a value.)

2.2 #lang iracket/lang🔗ℹ

 #lang iracket/lang package: iracket

In addition to being interpreted specially by the IRacket kernel (see IRacket and Languages), iracket/lang can be used as a language in Racket code. The purpose of the language is for testing how code should work in a Jupyter notebook; the iracket/lang language is not useful for developing normal Racket libraries and programs.

As a language, it behaves similarly to racket/load. Like racket/load, it evaluates body forms one by one in a top-level namespace. Unlike racket/load, it allows controlling the reader, and it delays reading the module body until run time, so that if one expression dynamically changes the reader, it affects the reading of the rest of the module body.

The following inconsistencies between Racket’s interpretation of the iracket/lang language and IRacket’s interpretation of a #lang iracket/lang declaration are known:
  • IRacket allows multiple #lang iracket/lang declarations in a notebook, but a Racket module does not (unless you change reader parameters, but then it still means something different).

  • Notebook cell boundaries can affect reader behavior, because the reader stops at the end of each cell. Thus a module formed by simply concatenating cell contents might behave differently.

  • When IRacket processes a #lang iracket/lang declaration, it does not reset parameters like current-readtable, read-accept-dot, etc. In contrast, Racket generally uses a fixed set of parameter values for reading modules (see syntax/modread).

Added in version 1.2 of package iracket.