Thanks so much! You’re always extremely helpful! I appreciate it.
The final result looks like this:
typeFromString :: String -> Q Type
typeFromString "Text" = [t| T.Text |] -- TODO: We shouldn't special case this. Figure out how to keep imported types under control.
typeFromString s = do
maybeType <- lookupTypeName s
case maybeType of
Just name -> return (ConT name)
Nothing -> do
if "Maybe " `L.isPrefixOf` s
then do
let innerType = drop 6 s
inner <- typeFromString innerType
return (AppT (ConT ''Maybe) inner)
else if "Either " `L.isPrefixOf` s
then do
let (left: right:_) = tail (words s)
lhs <- typeFromString left
rhs <- typeFromString right
return (AppT (AppT (ConT ''Either) lhs) rhs)
else fail $ "Unsupported type: " ++ s
declareColumns :: DataFrame -> DecsQ
declareColumns df = let
names = (map fst . L.sortBy (compare `on` snd). M.toList . columnIndices) df
types = map (columnTypeString . (`unsafeGetColumn` df)) names
specs = zip names types
in fmap concat $ forM specs $ \(nm, tyStr) -> do
ty <- typeFromString tyStr
let n = mkName (T.unpack nm)
sig <- sigD n [t| Expr $(pure ty) |]
val <- valD (varP n) (normalB [| col $(TH.lift nm) |]) []
pure [sig, val]
And then I include the following in my .ghci as a helper function:
:set -XOverloadedStrings
:set -XTemplateHaskell
:def! cols \s -> return ("_ = (); declareColumns " ++ s)
Then in the session I have:
ghci> df <- D.readCsv "./data/housing.csv"
ghci> import DataFrame.Functions (declareColumns)
ghci> :cols df
This creates all the references. Sadly adds an explicit template haskell dependency.