+We're going to build a record selector that looks like this:
+
+ data T a b c = T1 { op :: a, ...}
+ | T2 { op :: a, ...}
+ | T3
+
+ sel :: forall a b c. T a b c -> a
+ sel = /\ a b c -> \ T1 { sel = x } -> x
+ T2 { sel = 2 } -> x
+
+Note that the selector Id itself is used as the field
+label; it has to be an Id, you see!
+
+\begin{code}
+mkRecordSelector tycon fields@((first_con, first_field_label) : other_fields)
+ = let
+ field_ty = fieldLabelType first_field_label
+ field_name = fieldLabelName first_field_label
+ other_tys = [fieldLabelType fl | (_, fl) <- other_fields]
+ (tyvars, _, _, _) = dataConSig first_con
+ data_ty = applyTyCon tycon (mkTyVarTys tyvars)
+ -- tyvars of first_con may be free in field_ty
+ in
+
+ -- Check that all the fields in the group have the same type
+ -- This check assumes that all the constructors of a given
+ -- data type use the same type variables
+ checkTc (all (eqTy field_ty) other_tys)
+ (fieldTypeMisMatch field_name) `thenTc_`
+
+ -- Create an Id for the field itself
+ tcInstTyVars tyvars `thenNF_Tc` \ (tyvars', tyvar_tys, tenv) ->
+ tcInstType tenv field_ty `thenNF_Tc` \ field_ty' ->
+ let
+ data_ty' = applyTyCon tycon tyvar_tys
+ in
+ newLocalId SLIT("x") field_ty' `thenNF_Tc` \ field_id ->
+ newLocalId SLIT("r") data_ty' `thenNF_Tc` \ record_id ->
+
+ -- Now build the selector
+ let
+ selector_ty :: Type
+ selector_ty = mkForAllTys tyvars $
+ mkFunTy data_ty $
+ field_ty
+
+ selector_id :: Id
+ selector_id = mkRecordSelId first_field_label selector_ty
+
+ -- HsSyn is dreadfully verbose for defining the selector!
+ selector_rhs = mkHsTyLam tyvars' $
+ HsLam $
+ PatMatch (VarPat record_id) $
+ SimpleMatch $
+ selector_body
+
+ selector_body = HsCase (HsVar record_id) (map mk_match fields) (getSrcLoc tycon)
+
+ mk_match (con_id, field_label)
+ = PatMatch (RecPat con_id data_ty' [(selector_id, VarPat field_id, False)]) $
+ SimpleMatch $
+ HsVar field_id
+ in
+ returnTc (selector_id, if isLocallyDefinedName (getName tycon)
+ then VarMonoBind (RealId selector_id) selector_rhs
+ else EmptyMonoBinds)
+\end{code}