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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions