Opened 12 months ago

Last modified 2 months ago

#15650 new feature request

Add (or document if already exist) ability to derive custom typeclasses via source plugins

Reported by: chshersh Owned by:
Priority: normal Milestone: 8.6.1
Component: Compiler Version: 8.6.1-beta1
Keywords: source plugins, deriving, typeclass Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

Problem

Suppose, I have some custom typeclass Foo defined in some library foo:

class Foo a where
    ... some methods ...

I would like to be able to derive instances of this typeclass for any possible data type using deriving clause just like GHC already does for typeclasses Eq, Ord, Show, Read, Enum, etc.:

data Bar = Bar | Baz
    deriving (Eq, Ord, Foo)

There're already two possible ways to derive instances of custom typeclasses:

  1. anyclass deriving strategy (usually involves Generic)
  2. -XTemplateHaskell solution.

But I would like to have source-plugin-based solution for this problem so I can just add -fplugin=Foo.Plugin and enjoy deriving capabilities.

Advantage over existing approaches

Solution with -XTemplateHaskell is not that pleasant to write and easy to maintain (you need to use libraries like http://hackage.haskell.org/package/th-abstraction to support multiple GHC versions),involves scoping restriction and is syntactically uglier. Compare:

{-# LANGUAGE TemplateHaskell #-}

data Bar = Bar | Baz
    deriving (Eq, Ord)

deriveFoo ''Bar

Solution with something like Generic introduces performance overhead (required for to/from generic representation conversion). This might not be significant for something like parsing CLI arguments but it's more important if you want to have efficient binary serialisation.

Also, it's known that deriving typeclasses is a relatively slow compilation process (https://github.com/tfausak/tfausak.github.io/issues/127) so there's slight chance that deriving typeclass manually can be slightly faster than deriving Generic + MyClass. Especially when maintainers of plugins can experiment with some caching strategies for deriving typeclasses.

Change History (4)

comment:1 Changed 12 months ago by RyanGlScott

I am far from an expert on source plugins, so I can't say whether this is possible or not. But my initial reaction is: sure, why not? I could imagine an API like this:

type Derived a = a

class Foo a where ...

data Bar deriving (Derived Foo)

Here, the use of Derived is a syntactic clue to a source plugin to derive this using some custom functionality (instead of just trying to derive Foo normally).

To make this robust, you'd likely need to borrow some of GHC's own logic for deriving type classes. Luckily, GHC already exposes much of this! See the TcDeriv, TcGenDeriv, and TcDerivUtils modules.

I certainly don't have the time to try this out myself, but I'd be happy point any volunteers in the right direction.

comment:2 Changed 12 months ago by RyanGlScott

comment:3 Changed 12 months ago by mpickering

I have been playing around with this problem this morning. There are some engineering issues to do with the phase ordering. Plugins run at the end of the specific phase so you need to at least run a renamer plugin to remove the instances like Derived Foo from the deriving list. Then you probably need to also to implement a type checker plugin to solve the instances you are yet to create and finally, actually generate the instances with TcDeriv and so on.

One way around this might be to implement the deriving all in a renamer plugin as then you can just directly generate the instance Foo a where.. syntax and pass it into the type checker. Now typing this out, this seems a more robust and easy solution to implement.

comment:4 Changed 2 months ago by Marge Bot <ben+marge-bot@…>

In ff996555/ghc:

Add module doc for Plugins.

This was requested in #15650.
Note: See TracTickets for help on using tickets.