Looking for a very simple type family

I’m looking for a type family, giving me the type of a field in a record verifying
HasField symbol record type

I know this should theoretically exist given the functional dependency defined in the class :
class HasField (x :: k) r a | x r -> a where getField :: r -> a

But does it exist indeed ?

e.g.

data SomeData = SomeData { foo :: Int, bar :: [Int] }
type WhatTheFoo = GetType "foo" SomeData -- == Int
type WhatTheBar = GetType "bar" SomeData -- == [Int]

Do you need this to be a type family? Why not use template haskell for this?

In general, functional dependencies do not automatically lead to the definition of type families, because functional dependencies are purely about type inference and don’t carry evidence for type equality. Nor does GHC currently define such a type family for HasField (though some iterations of HasField-like designs had one). The rationale for the current design is explained here:

https://ghc-proposals.readthedocs.io/en/latest/proposals/0583-hasfield-redesign.html#functional-dependencies

1 Like

Thanks, that’s very illuminating indeed. What I don’t understand is : why not both ?
I hear the arguments are about “the core” of the HasField approach. What people and the compiler should rely on when using this machinery, but then, in the simple cases where we can deduce the type from the record and field, why can’t we expose this to the users ?
Something with a Maybe kind response perhaps, as a witness counterpart to the evidence-less functional dependency ?

As for for template haskell @sgraf I try not to use any template haskell, I’ve had bad experiences with it in the past, and I find it rather complex. My use case seems to fit the “type level functions” area a lot more than the “meta-programming” one.

HasField constraints are not actually declared, but created on the fly. (So they’re not “very simple” as requested by your o.p.) Note that you might export your datatype/its named fields to another module which isn’t using the HasField mechanism.

Adam’s pointed to the rationale for the design decisions at the time. IIRC, it was pretty finely balanced whether to use FunDeps or Type Families. One factor might be that TFs aren’t compatible with OverlappingInstances. Having both would be too much namespace pollution, wouldn’t it?

Right, there’s a wide design space here and other points in that space are perfectly plausible.

Another option for you might be to use generics. Provided your records have Generic instances, it should be possible to implement the GetType type family by computation on the result of Rep, then implement a version of getField that works through the generic representation.

1 Like

Right, because extensible records implementations usually have a GetField class whose instances are (1) check if the head is the field you want, or (2) recurse down the tail in search of it. Those are overlapping instances and you rely on GHC picking the more specific instance. (Was studying this literally yesterday so it’s fresh in my cache.)