Skip to content

Pin Data.Unit.unit to the shared foreign singleton (@inline unit never) - #14

Merged
Unisay merged 2 commits into
masterfrom
issue-176/unit-inline-never
Jul 12, 2026
Merged

Pin Data.Unit.unit to the shared foreign singleton (@inline unit never)#14
Unisay merged 2 commits into
masterfrom
issue-176/unit-inline-never

Conversation

@Unisay

@Unisay Unisay commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Data/Unit.purs opened with -- @inline unit always, on the assumption that unit is the cheapest possible thing to inline. That assumption is backwards: the FFI value unit = ({}) is a table constructor, so under always the compiler was free to paste a fresh {} allocation into every use site instead of sharing one table. This flips the pragma to never, so unit stays a single binding onto the foreign module.

Why never, and why now

Since purescript-lua/purescript-lua#175 the compiler no longer inlines foreign-module expressions at all. A foreign table stays hoisted and every access folds into a field read off it, so the singleton is already shared by default. That makes this flip a declaration of intent rather than the fix itself: it pins unit to one binding explicitly, so a later change to the inlining heuristics can't quietly start duplicating the constructor again. purescript#175 added @inline <name> never support for foreign exports precisely so a fork can state this.

What changes in generated code

Linking the compiler's test corpus against this branch, holding everything else fixed:

  • Runtime output is byte-identical. Every eval oracle passes unchanged, including the Array Unit printing class (Arrays of type Array Unit are always empty purescript-lua#23), which still prints unit / unit / unit / 3.
  • The singleton is preserved. Each linked module still holds exactly one {} for unit, inside the hoisted foreign table (local Data_Unit_foreign = { unit = {} }), and the total {} count across the linked goldens does not change.
  • Per-site field reads collapse to one shared binding. Where always folded the accessor into each site, never keeps a single local Data_Unit_unit = Data_Unit_foreign.unit and references it by name:
 local Data_Unit_foreign = { unit = {} }
+local Data_Unit_unit = Data_Unit_foreign.unit
 ...
-return { main = Effect_applicativeEffect.pure(Data_Unit_foreign.unit) }
+return { main = Effect_applicativeEffect.pure(Data_Unit_unit) }

Notes

  • The compiler-side golden churn lands separately, in the pslua repo, once this fork is released and the package set is bumped.
  • No .lua FFI changed, so luacheck is untouched, and ./scripts/build passes.

Fixes purescript-lua/purescript-lua#176

The FFI value unit = ({}) is a table constructor, not a value, so the
old `@inline unit always` pragma pasted a fresh {} allocation into every
use site (7 inlined {} in the linked Data.Array output alone, including
void = map(function() return {} end): one allocation per void step).

Flip the pragma to `@inline unit never` so unit stays one shared table
bound to the foreign module. This matches the foreign table-constructor
sharing the compiler performs by default since
purescript-lua/purescript-lua#175, and declares the sharing intent
explicitly against future heuristic changes.

Refs purescript-lua/purescript-lua#176
@Unisay
Unisay requested a review from Copilot July 12, 2026 18:49
@Unisay Unisay self-assigned this Jul 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Pins Data.Unit.unit to a single shared foreign singleton by changing the inlining pragma from always to never, preventing repeated {} allocations should foreign inlining heuristics change in the future.

Changes:

  • Updated Data.Unit’s inlining pragma to -- @inline unit never.
  • Added a changelog fragment documenting the rationale and behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Data/Unit.purs Switches the unit inlining pragma to never to keep unit as a single binding.
changelog.d/20260712_165906_unisay.md Documents the change and the allocation-sharing motivation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread changelog.d/20260712_165906_unisay.md Outdated
- changelog.d/20260712_165906_unisay.md:7 — keep `@inline unit never` code
  span on one line (#14 discussion_r3566905136)
@Unisay
Unisay merged commit 9154483 into master Jul 12, 2026
1 check passed
@Unisay
Unisay deleted the issue-176/unit-inline-never branch July 12, 2026 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[fork-ffi] prelude: flip @inline unit always to never — inlining unit duplicates the singleton allocation

2 participants