Opened 2 years ago

Last modified 2 years ago

#13957 new feature request

Allow deriving multiparameter type classes with representationally equal arguments

Reported by: Iceland_jack Owned by:
Priority: normal Milestone:
Component: Compiler Version: 8.0.1
Keywords: deriving 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 (last modified by Iceland_jack)

This currently works

{-# Language DerivingStrategies, MultiParamTypeClasses, FunctionalDependencies, GeneralizedNewtypeDeriving #-}

import Data.Kind
import Data.Functor.Identity

class SIEVE f p | p -> f where
  sIEVE :: p a b -> a -> f b

instance SIEVE Identity (->) where
  sIEVE = (Identity .)

newtype ARR a b = ARR (a -> b)
  deriving newtype 
    (SIEVE Identity)

But what if I want a Sieve I ARR instance, for newtype I a = I a (which is representationally equal to newtype Identity a = Identity a)?

{-# Language DerivingStrategies, MultiParamTypeClasses, FunctionalDependencies, GeneralizedNewtypeDeriving, RankNTypes, TypeApplications, ScopedTypeVariables, InstanceSigs #-}

import Data.Kind
import Data.Functor.Identity
import Data.Coerce

class SIEVE f p | p -> f where
  sIEVE :: p a b -> a -> f b

instance SIEVE Identity (->) where
  sIEVE = (Identity .)

newtype ARR a b = ARR (a -> b)
  deriving newtype
    (SIEVE I)

newtype I a = I a

generating the following code (this is basically to code generated before, except replacing some Identitys with Is

instance SIEVE I ARR where
  sIEVE :: forall a b. ARR a b -> (a -> I b) 
  sIEVE
    = coerce
      @((a -> b) -> a -> Identity b)
      @(ARR a b  -> a -> I b)
      sIEVE

GHC should be able to recover Identity due to the functional dependency.

Change History (5)

comment:1 Changed 2 years ago by Iceland_jack

TODO What if the argument order of SIEVE were reversed, as with Sieve.. let's find a way to allow deriving that as well.

comment:2 Changed 2 years ago by Iceland_jack

Description: modified (diff)

comment:3 Changed 2 years ago by RyanGlScott

This doesn't strike me as a very good idea, for a simple reason: it's quite possible you might also have instance SIEVE I (->) in scope, which might behave differently from instance SIEVE Identity (->). Allowing this sort of thing would make deriving with MPTCs very unpredictable.

Last edited 2 years ago by RyanGlScott (previous) (diff)

comment:4 in reply to:  3 Changed 2 years ago by Iceland_jack

We cannot both have SIEVE I (->) and SIEVE Identity (->) if we assume a functional dependency, similar to #13404 where an analogous example would compile

class SIEVE p where
  type SIEVE_ty p :: Type -> Type
  sIEVE :: p a b -> (a -> SIEVE_ty p b)

instance SIEVE (->) where
  type SIEVE_ty (->) = Identity
  sIEVE :: (a -> b) -> (a -> Identity b)
  sIEVE f = f >>> Identity

newtype ARR a b = ARR (a -> b)
  deriving newtype
    SIEVE

ignore

Last edited 2 years ago by Iceland_jack (previous) (diff)

comment:5 Changed 2 years ago by RyanGlScott

Keywords: deriving added
Note: See TracTickets for help on using tickets.