Opened 8 years ago

Last modified 7 days ago

#1830 new feature request

Automatic derivation of Lift

Reported by: guest Owned by: RyanGlScott
Priority: normal Milestone:
Component: Template Haskell Version: 6.8.1
Keywords: Cc: alfonso.acosta@…, vogt.adam@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Revisions: Phab:D1168


This feature request was brought up in this template-haskell thread[1]:

There are apparently two implementation alternatives:

1) Merge Ian's th-lift library[2] with the mainstream template-haskell library

2) Implement "instance Data a => Lift a" (requires allowing undecidable and overlapping instances) in template-haskell. Since Data can be derived automatically by GHC, such an instance would imply automatic derivation of Lift as well.

[1] [2]

Change History (15)

comment:1 Changed 8 years ago by igloo

  • difficulty set to Unknown
  • Milestone set to 6.10 branch

We should aim to decide something in time for 6.10.

comment:3 Changed 7 years ago by simonmar

  • Architecture changed from Unknown to Unknown/Multiple

comment:4 Changed 7 years ago by simonmar

  • Operating System changed from Unknown to Unknown/Multiple

comment:5 Changed 6 years ago by igloo

  • Milestone changed from 6.10 branch to _|_

comment:6 Changed 5 years ago by aavogt

  • Cc vogt.adam@… added
  • Type of failure set to None/Unknown

comment:7 Changed 3 years ago by morabbin

Bump; worth looking at merging th-lift again?

comment:8 Changed 10 days ago by RyanGlScott

  • Differential Revisions set to Phab:D1168
  • Owner set to RyanGlScott

I have a differential ( Phab:D1168 ) submitted which implements this feature, but I would like some feedback. I'll copy over my questions from the Phabricator page:

  1. What should we do if someone attempts to derive Lift on a datatype with no constructors? I (somewhat arbitrarily) chose it to generate code like this:
data Void deriving Lift

instance Lift Void where
    lift _ = appE (varE 'error) (litE (stringL "Void lift"))

but lift _ = error "Void lift" would also work.

  1. How should primitive data types be handled? I adapted the approach Show takes, which has special cases for Char#, Float#, Double#, Int#, and Word#. Note that template-haskell also has a StringPrimL constructor, so we could handle Addr# as well, but there's not currently a function of type String -> [Word8] in base that I could use in conjunction with it (there is in ghc, but I'm not sure if it's verboten to put GHC internals in derived code).

comment:9 Changed 10 days ago by rwbarton

What should we do if someone attempts to derive Lift on a datatype with no constructors?

Since lift then has type Void -> Q Exp, I would be inclined to define it as absurd, i.e., an empty case. I don't see the point of turning it into a runtime error when lift (error "foo") is a splice-time error for an inhabited type.

comment:10 follow-up: Changed 10 days ago by goldfire

Thanks for taking this on, Ryan, but I'm unconvinced that GHC needs built-in support here. What's wrong with using th-lift? I mean, I agree that in theory, DeriveLift would be nice, but it's Yet Another Thing. If something can be done well outside of GHC, I think it should be. A common argument here is that if a feature is built-in (as opposed to requiring TH), then users don't have to use TH. But that argument falls flat here, because users are clearly already using TH!

comment:11 in reply to: ↑ 10 Changed 10 days ago by RyanGlScott

Replying to goldfire:

What's wrong with using th-lift?

Type inference. th-lift is fundamentally limited in that it cannot infer the correct instance context for complex data structures such as

data Nested f a = Nested (f a) deriving Lift

-- instance Lift (f a) => Lift (Nested f a) where ...


data R a = R a

instance (Show a, Lift a) => Lift (R a) where ...

data Restricted a = Restricted (R a) deriving Lift

-- instance (Show a, Lift a) => Lift (Restricted a) where ...

Granted, this could be improved if th-lift were changed to allow something like this:

instance (Show a, Lift a) => Lift (Restricted a) where
    lift = $(makeLift ''Restricted)

But a deriving statement is infinitely more aesthetically pleasing, in my opinion.

But that argument falls flat here, because users are clearly already using TH!

Well, they're using the template-haskell library, but not necessarily the TemplateHaskell extension. Users may want to define Lift instances for data types without actually using the TemplateHaskell extension (as doing so precludes a library from being built on a stage-1 compiler). Having access to DeriveLift allows a library to be built with Lift support on GHC stage 1 and 2, and then downstream packages that depend on the library can decide if they want to actually utilize the Lift instances by means of the TemplateHaskell extension.

comment:12 follow-up: Changed 9 days ago by mpickering

Is it possible to derive these instances using GHC.Generics? Then the DeriveAnyClass extension could be used for the same syntactic effect without modifying the compiler.

comment:13 in reply to: ↑ 12 Changed 9 days ago by RyanGlScott

Replying to mpickering:

Is it possible to derive these instances using GHC.Generics?

I believe so, although with GHC.Generics you cannot have special cases for unlifted types like Int# (currently, the Phab differential uses the Lit type from template-haskell to deal with them).

comment:14 Changed 8 days ago by simonpj

I'm not dead set against this (adding new auto-derived classes doesn't interact with anything), but it does impose a little extra maintenance burden to keep it all working and you can get nearly the same behaviour with a (perhaps enhance) th-lift library.

It would seem more persuasive if there was more than one advocate for it. Is there a large constituency of TH users who will benefit from this? Data from Hackage might be illuminating... how many Lift instances exist?

comment:15 Changed 7 days ago by RyanGlScott

According to packdeps, 32 libraries immediately depend on th-lift. Doing a grep of hackage-packages for files that contain both Language.Haskell.TH.Syntax and instance Lift reveals that these additional libraries define their own Lift instances:


This obviously isn't scientifically rigorous, as there is a chance that the instance Lift could be using some other Lift, or that the Lift instances in those packages wouldn't be derivable in the first place. But this at least gives an approximation of how much impact deriving Lift might have.

Note: See TracTickets for help on using tickets.