1

I'm calling my TH function, called createRecordSplice in the following manner:

data User = User {userFoo :: String, userBar :: Text} deriving (Eq, Show, Generic)
createRecordSplice "user" ''User ['userBar] "NewUser" "nuser"

Notice that the record selector userBar is passed with a single-quote.

Now, in my TH function where I want to check the field names of User against userBar, I'm encountering a strange field name which contains $sel, due to which, my main logic is failing to work.

(Common.TestRecordSplices.userFoo,[Common.TestRecordSplices.$sel:userBar:User])
(Common.TestRecordSplices.userBar,[Common.TestRecordSplices.$sel:userBar:User])

What is this $sel:userBar:User and how does one convert userBar to $sel:userBar:User, or vice versa, without resorting to string manipulation?

My TH function:

createRecordSplice :: String -> Name -> [Name] -> String -> String -> Q [Dec]
createRecordSplice sourcePrefix record requiredFields newRectype targetPrefix = reify record >>= \case
  (TyConI (DataD [] recordConstr [] knd constrs classes)) -> case constrs of
    [(RecC _ sourceFields)] -> do
      let newFields = DL.foldl'
                      (\memo (fname, bang, ftype) ->

                         -- OUTPUT FOR THIS TRACE IS GIVEN ABOVE
                         trace (show (nameBase fname, nameBase <$> requiredFields)) $ 

                         if (fname `elem` requiredFields)
                         then (sourceToTargetName fname, bang, ftype):memo
                         else memo
                      ) [] sourceFields
      runIO $ putStrLn (show newFields)
      pure $ [DataD [] (mkName newRectype) [] knd [RecC (mkName newRectype) newFields] classes]
    _ -> fail $ "creating record splices for types with multiple constructors (sum types), i.e. data X = Y | Z, is not supported yet"
  where
    sourceToTargetName n = mkName $ targetPrefix ++ (DL.drop (DL.length sourcePrefix) (nameBase n))
Saurabh Nanda
  • 6,373
  • 5
  • 31
  • 60
  • The question here is unclear to me. What is the "main logic" which is failing to work? `$sel:userBar:User` is the pretty printed representation of some part of the TH AST (presumably, it is the overloaded record label for the type `User` whose name is `userBar`). You convert a string to an ADT by parsing it, but I don't see why you would need to do so (just manipulate the TH AST directly?). (Aside: if you are ever relying on the `Show` instance of the TH AST for anything but debugging, you're doing it wrong - just splice the `Q [Dec]` where you need to use it.) – user2407038 Aug 14 '17 at 20:07
  • I **am** manipulating the Ast directly at `if (fname elem requiredFields)` but it is not working because `requiredFields`, upon inspection seems to have a `$sel` in it whereas `fname` doesnt. And both of them refer to the same thing, conceptually! – Saurabh Nanda Aug 15 '17 at 04:24
  • Well, as you've discovered, they are not the same thing, regardless of your desire to treat them as equal elements (a record field is not the same thing as the projection function generated from that record field, despite the fact that they use the same identifier in concrete Haskell code). I take it the problem then is that `elem fname requiredFields` is `False` when you want it to be `True`? If so, define your own comparison function `cmp :: Name -> Name -> Bool` which has the desired semantics and replace `elem` with `any . cmp`. – user2407038 Aug 15 '17 at 06:30

0 Answers0