Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template Haskell and Implicit Parameters

Is there a way to create functions with implicit parameters or let bindings with implicit parameters using template haskell?

I.e. is it possible to generate a signature like this using template haskell:

 doSomething :: (?context :: Context) => m a

Or an invocation like this:

 invoc = let ?context = newContext in doSomething

I could not find suitable algebraic data types nor any functions which would help me out on this topic in the API documentation for template haskell. I'm using GHC 7.4.2.

If there is no native support for this extension in template haskell, is there some other possibility to inject code during compilation (maybe something like a general “code injection function” within template haskell?).

EDIT: I tried the suggestion from the comments, this is what happens:

runQ [d| f :: (?c :: String) => Int ; f = 7 |]

<interactive>:10:17: parse error on input `c'

whereas this works:

 runQ [d| f :: Int ; f = 7|]
 [SigD f_0 (ConT GHC.Types.Int),ValD (VarP f_0) (NormalB (LitE (IntegerL 7))) []]

doesn't seem to be supported.

like image 798
scravy Avatar asked Jan 10 '13 17:01

scravy


1 Answers

Here's one way that's pretty fragile, but sort of works. While you can't refer to ?x in the Exp that template haskell uses, you can refer to a definition in another module like:

reserved_prefix_x = ?x

Below is some code that generates variables like above in one run of ghc, and in a second run of ghc the variables actually refer to implicit parameters.

{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}
module GenMod (h) where

import Data.Generics
import Data.IORef
import Data.List
import Language.Haskell.Meta.Parse as P
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import qualified Data.Set as S
import qualified Language.Haskell.Exts.QQ as Q
import System.IO.Unsafe

h = Q.hs { quoteExp = \s -> do
    r <- either fail (upVars . return) (P.parseExp s)
    writeMod'
    return r
    }

pfx = "q_"

{-# NOINLINE vars #-}
vars :: IORef (S.Set String)
vars = unsafePerformIO (newIORef S.empty)

writeMod' = runIO $ writeFile "GEN.hs" . ppMod =<< readIORef vars

writeMod = -- might be needed to avoid multiple calls to writeFile?
           -- in this example this is called for every use of `h'
    QuasiQuoter { quoteDec = \ _ -> do
                    writeMod'
                    [d| _ = () |] }

ppMod xs = "{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}\n\
            \module GEN where\n" ++
    unlines (map (\x -> pfx ++ x ++ " = ?" ++ x) (S.toList xs))

upVars x = do
    x' <- x
    runIO $ modifyIORef vars (S.union (getMatchingVars x'))
    runIO $ print =<< readIORef vars
    return x'

getMatchingVars =
    everything
        S.union
        (mkQ S.empty
            (\ (OccName x) -> maybe S.empty S.singleton (stripPrefix pfx x)))

A Main.hs file that uses the quasiquoter GenMod.hs:

{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams, QuasiQuotes, TemplateHaskell, CPP #-}
import GenMod

#ifndef stage1
import GEN
#endif

f_ = [h| q_hithere |]

You have to call ghc twice, like:

ghci -Dstage1 Main.hs
GHCi, version 7.6.1: http://www.haskell.org/ghc/  :? for help
[1 of 2] Compiling GenMod           ( GenMod.hs, interpreted )
[2 of 2] Compiling Main             ( Ex.hs, interpreted )
fromList ["hithere"]

Ex.hs:8:6: Not in scope: `q_hithere'
Failed, modules loaded: GenMod.

Though ghc fails, it still generates the GEN.hs which contains:

{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}
module GEN where
q_hithere = ?hithere

Which will be there when you load Main (leaving out the -D flag)

*Main> :t f_
f_ :: (?hithere::t) => t

This kind of trouble probably isn't worth it. Maybe other situations of calling out to other programs from TH are more motivating such as inline calls to other languages http://hpaste.org/50837 (gfortran example)

Since I used haskell-src-meta's default parser, the quasiquote gets to use variables "reserved_prefix_x" not "?x". It should be possible to accept the "?x" without too much difficulty.

like image 156
aavogt Avatar answered Nov 06 '22 11:11

aavogt