| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
FWIW - i was able to build this and to build my unmodified repos and apps with the modified purs and also to patch my code to the new syntax and build and test that successfully. |
Sorry, something went wrong.
There was a problem hiding this comment.
Notes on the less obvious parts.
Sorry, something went wrong.
| _ -> pure decl | ||
| DeriveClause sa _ddt tyName tyVars className extraArgs DerivedInstance | ||
| | className == Libs.Generic || className == Libs.Newtype -> | ||
| deriveInstance mn ds (expandDeriveClause mn sa tyName tyVars className extraArgs DerivedInstance) |
There was a problem hiding this comment.
Generic and Newtype derive clauses get expanded early here because deriveInstance needs to fill in the wildcard (the representation type) by inspecting the data constructors. That has to happen before the general DeriveClause expansion in TypeClasses.hs.
Sorry, something went wrong.
| -> [SourceConstraint] | ||
| -> [FunctionalDependency] | ||
| -> m Expr | ||
| deriveViaInstance mn className tys viaTy _typeClassArguments _typeClassSuperclasses _typeClassDependencies = do |
There was a problem hiding this comment.
Validates three things before emitting a DeferredDictionary that swaps the instance's last type arg with the via type:
Sorry, something went wrong.
| _ -> internalError "typesOf did not return a singleton" | ||
| go ValueDeclaration{} = internalError "Binders were not desugared" | ||
| go BoundValueDeclaration{} = internalError "BoundValueDeclaration should be desugared" | ||
| go DeriveClause{} = internalError "DeriveClause should be desugared" |
There was a problem hiding this comment.
DeriveClause is always desugared before type checking, but GHC's -Wincomplete-patterns with -Werror (used in CI via stack) requires the case.
Sorry, something went wrong.
|
Closing in favour of a smaller, focused PR without derive via. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
What this enables
Derive clauses can now be written directly on type declarations:
Newtype deriving and deriving via work too:
Multiple classes per clause, multiple clauses per type, and you can mix with standalone derive instance freely.
Why
Long-requested: #3426, #1891. The standalone derive instance syntax gets noisy fast, #3426 has a good side-by-side showing how 4 simple enum types balloon into 28 instance declarations.
Prior art
This PR also includes standalone derive via, building on @kl0tl's work in #3824 (2020). That PR mapped out the key error cases, floating type variables, kind mismatches, non-coercible via types, superclass verification, and I handle the same cases here.
I diverged from #3824 in a few places: I kept via context-sensitive (not a keyword token) so it still works as a variable name, added ViaInstance as its own constructor rather than modifying DerivedInstance, and preserved existing error names instead of renaming them.
The deriving clause syntax itself is new and not part of #3824.
Closes #3426.
Implementation
This is a desugaring pass. Deriving clauses get parsed into a temporary DeriveClause AST node, then expanded into regular TypeInstanceDeclarations during type class desugaring. Everything downstream — type checker, deriving solver, codegen — sees ordinary instance declarations and doesn't need to change.
Design choices
derive and via are context-sensitive — neither is a reserved word, so existing code using them as identifiers or record labels is unaffected.
derive not deriving — mirrors PS's existing keyword (derive instance).
Strategy placement mirrors standalone syntax — derive newtype (Eq) matches derive newtype instance Eq T, and derive (Show) via T keeps via attached to the clause rather than the class.
Fundep-aware argument inference — for multi-param classes where all parameters after the first are determined by functional dependencies (like Generic a rep | a -> rep), wildcards are filled in automatically.
Shortcomings and future work
No constraint inference. For parameterized types, derive (Eq) on data Pair a = Pair a a generates derive instance Eq (Pair a) which fails — there's no Eq a constraint. This is the same limitation standalone derive has; the workaround is the same (use standalone syntax with an explicit constraint). A future improvement could infer constraints as GHC does.
Multi-param classes without fundeps need explicit args. Classes with 2+ parameters where the extra params aren't determined by functional dependencies require you to spell out the type arguments.
Open design question: via placement in standalone syntax
The standalone derive via syntax currently puts the strategy before instance, matching Haskell and the existing derive newtype instance pattern:
An alternative would be to always put via last, and unify newtype as via newtype:
This is more uniform — via always means "how to derive" and always comes at the end — but it would be a breaking change to the existing derive newtype instance syntax. Feedback welcome on which direction to go.