Skip to content

foreign block parser is line-oriented: :requires and multi-line type signatures must fit on the same line #52

Description

@kumavis

Summary

The foreign block parser tokenises its body line-by-line and rejects two natural multi-line shapes:

  1. :requires (Cap) annotation on a line below foreign — fails with foreign: Expected: (name [:as alias] : type), got: (:requires (NetCap)).
  2. Type signatures wrapped across lines — fails with foreign-type-tokens->sexp: Cannot parse foreign type tokens (per the pitfall doc).

Both forms parse fine when collapsed onto a single line. The result is awkward foreign lines that often run past 100 columns when broadcast propagators or capability-protected APIs need to be declared.

Filed from docs/tracking/2026-04-27_GOBLIN_PITFALLS.md § #20 and docs/tracking/2026-04-28_ETPROP_PITFALLS.md § #6.

Reproducer

Variant 1 — :requires on a separate line

ns r20

foreign racket "racket/base"
  :requires (NetCap)
  [string-length :as slen : String -> Int]

Actual (current main, Racket 9.1):

foreign: Expected: (name [:as alias] : type), got: (:requires (NetCap))

Workaround: collapse onto one line:

foreign racket "racket/base" :requires (NetCap) [string-length :as slen : String -> Int]

Variant 2 — multi-line type signature (per ETP-#6 pitfall doc)

The pitfall doc captures this shape:

foreign racket "racket/base"
  [net-add-prop : Posit32 Posit32 Posit32
                 [List [List Posit32]] [List Posit32] Posit32 -> Posit32]

foreign-type-tokens->sexp: Cannot parse foreign type tokens

Workaround: a single (long) line:

[net-add-prop : Posit32 Posit32 Posit32 [List [List Posit32]] [List Posit32] Posit32 -> Posit32]

Why this matters

  • Broadcast propagators in the propagator-FFI take several [List ...] typed arguments; signatures naturally exceed 100 chars.
  • Capability protected APIs (:requires (Cap)) are exactly the right place to use line-continuation for readability — and they're the ones the parser refuses.

Suggested fix paths

The foreign form already lives in a WS-mode block context — the WS reader's existing indent-continuation could be applied to the foreign body. Smallest scope:

  1. Allow :requires (...) on the line directly below foreign (indented past the foreign keyword).
  2. Allow multi-token type signatures inside [name :as alias : ...] to wrap by treating the entire [...] form as a single token group regardless of internal newlines.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions