Ticket #1835 (closed feature request: fixed)

Opened 6 years ago

Last modified 3 years ago

Provide information of the instance environment

Reported by: guest Owned by: igloo
Priority: normal Milestone: _|_
Component: Template Haskell Version: 6.8.1
Keywords: Cc: alfonso.acosta@…, illissius@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty: Unknown
Test Case: th/T1835 Blocked By:
Blocking: Related Tickets:

Description

This feature request was brought up in [1].

Right now TH doesn't have access to the instance environment of the typechecker. Thus, it's impossible to check whether a class instance exists for certain type, making impossible to guarantee the generation of correct code in some cases.

For example, that happens when, for a justified reason, the function to be spliced takes a variable Name as argument and not a value. There is no way to force a class constraint on names nor a way to check whether the variable type (obtained with reify) is an instance of a certain class. If the generated code makes use of any class function, its possible to generate incorrect code.

I suggest adding a function similar to this one:

isClassInstance :: Name   -- ^ Class name
                -> Type   -- ^ Type to check
                -> Q Bool

Another option would be extending ClassI to provide the list of instances of a class and code 'isClassInstance' using that information.

[1]  http://www.haskell.org/pipermail/template-haskell/2007-November/000645.html

Attachments

1835-tests.patch Download (72.1 KB) - added by SamAnklesaria 3 years ago.
for the test suite
1835.patch Download (103.8 KB) - added by SamAnklesaria 3 years ago.
for the compiler
1835-th.patch Download (4.7 KB) - added by SamAnklesaria 3 years ago.
for template haskell
dph.patch Download (42.4 KB) - added by SamAnklesaria 3 years ago.
for dph

Change History

  Changed 6 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.10 branch

  Changed 5 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

  Changed 5 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple

  Changed 4 years ago by igloo

  • milestone changed from 6.10 branch to _|_

  Changed 3 years ago by Ashley Yakeley

  • failure set to None/Unknown

+1.

It would be more useful if the return type were Q [Decl], so we could see details of all instances of the given class that reference the given type.

  Changed 3 years ago by illissius

  • cc illissius@… added

  Changed 3 years ago by SamAnklesaria

  • owner set to SamAnklesaria

  Changed 3 years ago by SamAnklesaria

  • owner changed from SamAnklesaria to igloo

  Changed 3 years ago by igloo

  • status changed from new to patch

follow-up: ↓ 11   Changed 3 years ago by illissius

Is there any chance of this getting in in some form? Would be very nice to have. (I would've taken a shot at it myself, but got beaten to the punch.)

in reply to: ↑ 10   Changed 3 years ago by SamAnklesaria

Agreed. Anyone I could nag for this to occur?

Replying to illissius:

Is there any chance of this getting in in some form? Would be very nice to have. (I would've taken a shot at it myself, but got beaten to the punch.)

  Changed 3 years ago by igloo

  • owner igloo deleted

Thanks for the patch.

I took a look at it, and it doesn't look like it works how I'd expect for parameterised types. It also doesn't interact well with other GHC extensions. For example, this expanded version of your test:

{-# LANGUAGE TemplateHaskell, FlexibleInstances,
             MultiParamTypeClasses, TypeSynonymInstances #-}
module Main where
import Language.Haskell.TH
import Language.Haskell.TH.Syntax

class Eq a => MyClass a where myMeth :: a -> Int
data Foo = Foo deriving Eq
instance MyClass Foo where myMeth = const 1

data Bar = Bar
    deriving Eq
type Baz = Bar
instance MyClass Baz

data Quux a = Quux a
    deriving Eq
data Quux2 a = Quux2 a
    deriving Eq
instance Eq a => MyClass (Quux a)
instance Num a => MyClass (Quux2 a)

class MyClass2 a b
instance MyClass2 Int Bool

main = do
   print $(isClassInstance ''Eq (ConT ''Foo) >>= lift)
   print $(isClassInstance ''MyClass (ConT ''Foo) >>= lift)
   print $ not $(isClassInstance ''Show (ConT ''Foo) >>= lift)
   print $(isClassInstance ''MyClass (ConT ''Bar) >>= lift)
   print $(isClassInstance ''MyClass (ConT ''Baz) >>= lift)
   print $(isClassInstance ''MyClass (AppT (ConT ''Quux) (ConT ''Int)) >>= lift)
   print $(isClassInstance ''MyClass (AppT (ConT ''Quux2) (ConT ''Int)) >>= lift)
   print $(isClassInstance ''MyClass2 (ConT ''Int) >>= lift)
   print $(isClassInstance ''MyClass2 (ConT ''Bool) >>= lift)

prints

True
True
True
False
True
False
False
True
False

It might be worth taking a step back, and writing a wiki page on exactly what the behaviour should be.

  Changed 3 years ago by igloo

  • status changed from patch to new

  Changed 3 years ago by SamAnklesaria

  • owner set to SamAnklesaria

Changed 3 years ago by SamAnklesaria

for the test suite

Changed 3 years ago by SamAnklesaria

for the compiler

Changed 3 years ago by SamAnklesaria

for template haskell

Changed 3 years ago by SamAnklesaria

for dph

  Changed 3 years ago by SamAnklesaria

  • status changed from new to patch

  Changed 3 years ago by SamAnklesaria

  • owner changed from SamAnklesaria to igloo

  Changed 3 years ago by SamAnklesaria

To address multi-parameter type classes, isClassInstance now takes a list of types to check, not just one. Only a full set of instance arguments will return a true result. Type synonyms work as expected now as well.

  Changed 3 years ago by simonpj

Thanks for doing this work. We will definitely put this in GHC 6.14.

Apart from the implementation we just need to check that there is consensus about the design. You propose:

  • Extending the Quasi monad to have
    class (Monad m, Functor m) => Quasi m where
      ...
      qReify :: Name -> m Info
      qIsClassInstance :: Name -> [Type] -> m Bool
    
    isClassInstance :: Name -> [Type] -> Q Bool
    isClassInstance n ts = Q (qIsClassInstance n ts)
    
  • Extending the info you get back when reifying a Class, by adding its instances:
    data Dec = ...
      | ClassI Dec [([TyVarBndr],Cxt,[Type])]
    

Some random thoughts

  • In principle, I suppose that isClassInstance isn't really necessary: you could reify the class and search the list yourself. But I can see that it's convenient.
  • Currently isClassInstance returns True if any instances match, even if they overlap etc. Might you want to know more? And if so, what would the result type of isClassInstance be?

I'd like to see if the proposed design meets the needs of other Template Haskell users. Any comments from any other TH users? Even just "yea" would be good to know.

  Changed 3 years ago by SamAnklesaria

As far as the first issue goes, isClassInstance needs to be separate from the information returned by ClassI because of the situations you outlined before: type synonyms don't show up, and to search through parameterized instances would require unification. So it's easier just to use the built in class lookup functionality, and make ClassI and isClassInstance have nothing to do with each other, as I've done.

follow-up: ↓ 21   Changed 3 years ago by Ashley Yakeley

class C a

instance C a => C [a]

If I lookup an instance for "C [Int]", how I discover that "C Int" is context for it? Do I have to reify C and search its instances attempting to type-match?

in reply to: ↑ 20   Changed 3 years ago by SamAnklesaria

You can easily look up whether [Int] is an instance of C with isClassInstance. But there's no easy way at the moment to find out which instance makes that true; the most you can do, as you said, is reify the class to get all the instances and hunt around.

  Changed 3 years ago by illissius

Just having isClassInstance fulfills my immediate needs; I don't know what I'll want or need in the future. If I could, I'd like to have a pony and the ability to map in the reverse direction, i.e. from types to the classes they're instances of. (But presumably that can also be added separately later.) Thanks for implementing this.

  Changed 3 years ago by illissius

Don't mean to bug, but this is still going in, right?

  Changed 3 years ago by simonpj

A timely reminder, thank you. I'm doing it now.

  Changed 3 years ago by simonpj

  • status changed from patch to closed
  • testcase set to th/T1835
  • resolution set to fixed

OK done. Patches to GHC

Wed Sep 15 08:12:42 PDT 2010  simonpj@microsoft.com
  * Implement TH reification of instances (Trac #1835)
  
  Accompanying patch for template-haskell package is reqd

    M ./compiler/hsSyn/Convert.lhs -1 +6
    M ./compiler/rename/RnTypes.lhs -1 +1
    M ./compiler/typecheck/TcHsType.lhs -1 +1
    M ./compiler/typecheck/TcSplice.lhs -1 +48

and template-haskell package

Wed Sep 15 08:13:29 PDT 2010  simonpj@microsoft.com
  * Add TH reification of instances (Trac #1835)

    M ./Language/Haskell/TH/Ppr.hs -2 +11
    M ./Language/Haskell/TH/Syntax.hs -11 +41

and one for DPH.

Simon

  Changed 3 years ago by illissius

Thanks!

Note: See TracTickets for help on using tickets.