Injecting variables into a GHCi session

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.

4 Likes