1
votes

I can't get GHC to resolve the generic typeclass instance below: (The goal is to achieve the same effect as accessing the property x of point like x point, but, instead by utilizing type level strings)

data Label (l :: Symbol) =
  Get

class Has a l b | a l -> b where
  get :: a -> Label l -> b

instance (Generic r, GenericHas (Rep r) pl pt) => Has r pl pt where
  get = genericHas . from

class GenericHas a l b | a l -> b where
  genericHas :: a x -> Label l -> b

instance GenericHas (D1 a (C1 b (S1 (c ('Just pl) d e f) (Rec0 pt)))) pl pt where
  genericHas (M1 (M1 (M1 (K1 v)))) _ = v

data Point =
  Point
    { x :: Int
    }
  deriving (Show, Generic)

example :: Int
example = get (Point 1) (Get :: Label "x")

This below is the error:

    * No instance for (GenericHas
                         (D1
                            ('MetaData
                               "Point"
                               "Alba.Persistence"
                               "alba-0.1.0.0-w6KgEimatKAP1g0rWS7YT"
                               'False)
                            (C1
                               ('MetaCons "Point" 'PrefixI 'True)
                               (S1
                                  ('MetaSel
                                     ('Just "x")
                                     'NoSourceUnpackedness
                                     'NoSourceStrictness
                                     'DecidedLazy)
                                  (Rec0 Int))))
                         "x"
                         Int)
        arising from a use of `get'
    * In the expression: get (Point 1) (Get :: Label "x")
      In an equation for `example':
          example = get (Point 1) (Get :: Label "x")

1

1 Answers

3
votes

The problem is that GHC is unable to match c with 'MetaSel, because 'MetaSel is of a higher kind. By default fresh type variables are assumed to have kind *, so the matching fails here.

One way to fix this is to just replace c with 'MetaSel:

instance GenericHas (D1 a (C1 b (S1 (`MetaSel ('Just pl) d e f) (Rec0 pt)))) pl pt where

Another way is to enable PolyKinds. This will tell GHC not to assume kind * for fresh variables and the matching will succeed.


Bonus: you can have a nicer syntax with TypeApplications. You can write Get @"x" instead of Get :: Label "x". Or you can go even further and define a function that wraps the Get:

get' :: forall (l :: Symbol) a b. Has a l b => a -> b
get' a = get a (Get @l)

-- Usage:
example = get' @"x" (Point 1)