Skip to content

Add deriveEq for Plinth similar to deriving stock Eq#7433

Open
bezirg wants to merge 17 commits intomasterfrom
bezirg/derive_eq
Open

Add deriveEq for Plinth similar to deriving stock Eq#7433
bezirg wants to merge 17 commits intomasterfrom
bezirg/derive_eq

Conversation

@bezirg
Copy link
Contributor

@bezirg bezirg commented Nov 19, 2025

Context

  • What: Add Template Haskell deriveEq function to automatically generate PlutusTx.Eq instances
  • Why: Eliminate boilerplate and reduce errors from manual Eq instance implementation across plutus-ledger-api
  • Issue: Closes Add TH derivings for Plinth's Eq #7430

Approach

Implemented a Template Haskell function that generates structural equality similar to GHC's deriving stock Eq. The implementation:

  • Refactored module structure: Split PlutusTx.Eq into PlutusTx.Eq.Class (type class definition) and PlutusTx.Eq.TH (deriving function) for better organization
  • Smart variable naming: Generates unique variable names (e.g., l1l, r2r) to avoid shadowing warnings
  • Phantom type support: Uses role checking to exclude phantom type parameters from instance constraints, allowing derivation for types like PhantomADT e
  • Short-circuit evaluation: Generates efficient code that stops at the first inequality
  • INLINEABLE pragmas: Ensures cross-module optimization for on-chain code

The generated code handles:

  • Multi-constructor ADTs with catch-all clause for different constructors
  • Single-constructor types without unnecessary catch-all
  • Empty types (Void) with trivial True equality
  • Records and regular constructors uniformly

Changes

Template Haskell Implementation

  • plutus-tx/src/PlutusTx/Eq/TH.hs (NEW): Core deriveEq function using th-abstraction for reliable type introspection
  • plutus-tx/src/PlutusTx/Eq/Class.hs (NEW): Extracted Eq class definition from main module
  • plutus-tx/src/PlutusTx/Eq.hs: Refactored to re-export from submodules

Applied Across plutus-ledger-api

Replaced ~100 lines of manual Eq implementations with deriveEq calls in:

  • V1: Address, Credential, DCert, Tx, Value, Interval, Contexts
  • V2: Contexts, Tx
  • V3: Contexts, MintValue, Tx

Net result: -101 lines (602 deletions, 501 additions) primarily from removing boilerplate

Tests and Golden Files

  • plutus-tx/test/Eq/Spec.hs (NEW): Comprehensive test suite covering:
    • Multi-constructor ADTs with mixed field types
    • Newtypes
    • Records
    • Phantom types
    • Void types
    • Short-circuit evaluation behavior
  • Golden tests: TH code generation snapshots for SomeLargeADT and PhantomADT

Updated Benchmarks

  • Regenerated golden PIR/UPLC files reflecting new variable naming scheme

@bezirg bezirg changed the title Bezirg/derive eq Add deriveEq for Plinth similar to deriving stock Eq Nov 19, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 24, 2025

Execution Budget Golden Diff

670e913 (master) vs 3b9aaed

output

plutus-benchmark/cardano-loans/test/9.6/main.golden.eval

Metric Old New Δ%
CPU 111_100_889 110_972_889 -0.12%
Memory 622_350 621_550 -0.13%
Flat Size 8_663 8_660 -0.03%

plutus-benchmark/coop/test/9.6/mustBurnOwnSingleton.golden.eval

Metric Old New Δ%
CPU 114_113_035 114_049_035 -0.06%
Memory 579_805 579_405 -0.07%
Flat Size 3_833 3_830 -0.08%

plutus-benchmark/linear-vesting/test/9.6/main.golden.eval

Metric Old New Δ%
CPU 30_837_131 30_405_131 -1.40%
Memory 131_619 128_919 -2.05%
Flat Size 2_860 2_351 -17.80%

plutus-benchmark/script-contexts/test/V3/Data/9.6/purposeIsWellFormed-4.golden.eval

Metric Old New Δ%
Flat Size 1_839 724 -60.63%

This comment will get updated when changes are made.

@bezirg bezirg marked this pull request as ready for review November 24, 2025 10:07
@bezirg bezirg marked this pull request as draft November 24, 2025 10:07
@bezirg bezirg self-assigned this Nov 24, 2025
@bezirg bezirg force-pushed the bezirg/derive_eq branch 2 times, most recently from 5b30be7 to 5d931a0 Compare November 25, 2025 10:05
@bezirg bezirg marked this pull request as ready for review November 25, 2025 10:05
@bezirg bezirg requested review from SeungheonOh and Unisay and removed request for SeungheonOh November 25, 2025 10:05
@bezirg bezirg marked this pull request as draft November 25, 2025 13:42
@bezirg bezirg marked this pull request as ready for review November 25, 2025 13:50
@bezirg bezirg force-pushed the bezirg/derive_eq branch 5 times, most recently from 7593c53 to 3ca2a2e Compare November 26, 2025 11:18
Copy link
Collaborator

@SeungheonOh SeungheonOh left a comment

Choose a reason for hiding this comment

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

I'm not too confident in my ability to review TH code. So perhaps a look from @Unisay would be nice. But everything looks right from what I can tell.

@SeungheonOh
Copy link
Collaborator

Also, can you check if this works with polymorphic phantom types? I'm curious as per #4537

@Unisay
Copy link
Contributor

Unisay commented Dec 2, 2025

Perhaps in another PR?

If a PR is useful improvement even without its follow-up I'd say yes.

@IntersectMBO IntersectMBO deleted a comment from bezirg Feb 5, 2026
@IntersectMBO IntersectMBO deleted a comment from bezirg Feb 5, 2026
@IntersectMBO IntersectMBO deleted a comment from bezirg Feb 5, 2026
@Unisay Unisay force-pushed the bezirg/derive_eq branch 3 times, most recently from 8042476 to 7557d18 Compare February 9, 2026 14:04
@Unisay Unisay requested a review from SeungheonOh February 9, 2026 17:23
@Unisay Unisay requested a review from a team February 11, 2026 08:58
Copy link
Contributor

@ana-pantilie ana-pantilie left a comment

Choose a reason for hiding this comment

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

Approving because my comments aren't critical, but please take a look at them if you can.

Extract the Eq typeclass into PlutusTx.Eq.Class and add a new
PlutusTx.Eq.TH module providing deriveEq, a TH function that generates
PlutusTx.Eq instances analogous to GHC's `deriving stock Eq`.

This enables replacing hand-written structural equality instances with
a single `deriveEq ''TypeName` call, reducing boilerplate and ensuring
consistent pattern-matching with short-circuit evaluation and INLINABLE
pragmas across the codebase.
Add comprehensive tests covering product types, sum types, newtypes,
records, void types, phantom types, and recursive types. Golden tests
capture exact TH-generated code to detect regressions.

Fix goldenCodeGen to use `pretty @String` for proper Doc type
conversion needed by nestedGoldenVsDoc.
Replace hand-written Eq instances for Rational and These with
deriveEq calls. The generated instances are structurally identical
to the manual ones.
Replace hand-written PlutusTx.Eq instances across all V1, V2, and V3
modules (both Original and Data representations) with deriveEq calls.

MintValue retains its manual instance because its equality semantics
differ from structural equality (it normalizes zero-quantity entries).
Add golden TH tests capturing the exact generated Eq instances for
every type where manual instances were replaced with deriveEq. This
covers 42 types in plutus-ledger-api (V1/V2/V3, Original and Data
variants) and 2 types in plutus-tx (Rational, These).

These tests will detect any unintended changes to the generated
equality code.
Variable naming in deriveEq-generated code differs from manual
instances, causing golden file updates in plutus-benchmark and
plutus-tx-plugin test suites.
Add entries to plutus-tx and plutus-ledger-api changelogs documenting
the new deriveEq function and the migration from manual Eq instances.
Strip TH-generated unique name suffixes (7+ digits) from golden test
output for Data-variant types (V*D.*), which use TH.newName via asData
and produce non-deterministic constructor names across compilations.
The golden tests added by deriveEq require the 'diff' binary which is
not available in the Windows (mingW64) cross-compilation environment.
Replace formatTHOutput + TH.pprint with a new PlutusTx.Test.THPretty
module that uses prettyprinter's Wadler/Lindig algorithm with 100-column
page width. This produces much more readable golden test output -- lines
that previously stretched 200+ chars now wrap intelligently.
Replace commented-out deriveEq code with plain comments explaining
why PlutusTx.Eq instances are absent, addressing review feedback.
@Unisay Unisay enabled auto-merge (squash) February 13, 2026 10:11
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.

Add TH derivings for Plinth's Eq

5 participants