6

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.

scravy
  • 11,904
  • 14
  • 72
  • 127
  • 2
    Did you try quoting an expression using implicit parameters and seeing what AST you get? – C. A. McCann Jan 10 '13 at 18:30
  • Thanks, did not think about that; anyway it throws an error (see edited question). Does not seem to work. – scravy Jan 10 '13 at 18:35
  • Implicit parameters seems to have fallen out of the corpus of modern haskell idioms -- I'm not surprised if TH never got support for it. – luqui Jan 10 '13 at 18:41
  • @scravy: Yeah, if the quotation causes an error (make sure you have the extension enabled in all relevant modules, of course) that seems like a very good indication that it's not supported at all. – C. A. McCann Jan 10 '13 at 19:49
  • 3
    Also, just below the definition of the `Exp` type in the `Language.Haskell.TH.Syntax` module, there is [a comment which says "omitted: implicit parameters"](http://www.haskell.org/ghc/docs/7.4.2/html/libraries/template-haskell-2.7.0.0/src/Language-Haskell-TH-Syntax.html#line-879). – hammar Jan 10 '13 at 20:28

1 Answers1

3

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.

aavogt
  • 1,308
  • 6
  • 14