Шаблон Haskell и неявные параметры

Есть ли способ создавать функции с неявными параметрами или разрешать привязки с неявными параметрами с использованием шаблона haskell?

т.е. можно ли сгенерировать такую ​​подпись с помощью шаблона haskell:

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

Или такой вызов:

 invoc = let ?context = newContext in doSomething

Я не смог найти ни подходящих алгебраических типов данных, ни каких-либо функций, которые помогли бы мне в этой теме, в Документация API для шаблона haskell. Я использую GHC 7.4.2.

Если нет встроенной поддержки этого расширения в хаскеле шаблона, есть ли какая-то другая возможность внедрить код во время компиляции (может быть, что-то вроде общей «функции внедрения кода» в хаскеле шаблона?).

EDIT: Я попробовал предложение из комментариев, вот что происходит:

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

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

тогда как это работает:

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

вроде не поддерживается.


person scravy    schedule 10.01.2013    source источник
comment
Пробовали ли вы заключать выражение в кавычки с неявными параметрами и смотреть, какой AST вы получаете?   -  person C. A. McCann    schedule 10.01.2013
comment
Спасибо, не подумал об этом; в любом случае выдает ошибку (см. отредактированный вопрос). Не работает.   -  person scravy    schedule 10.01.2013
comment
Неявные параметры, кажется, выпали из корпуса современных идиом Haskell - я не удивлен, что TH никогда не получил для них поддержки.   -  person luqui    schedule 10.01.2013
comment
@scravy: Да, если цитата вызывает ошибку (конечно, убедитесь, что расширение включено во всех соответствующих модулях), это кажется очень хорошим признаком того, что оно вообще не поддерживается.   -  person C. A. McCann    schedule 10.01.2013
comment
Кроме того, чуть ниже определения типа Exp в модуле Language.Haskell.TH.Syntax находится комментарий опущен: неявные параметры.   -  person hammar    schedule 11.01.2013


Ответы (1)


Вот один из способов, который довольно хрупкий, но работает. Хотя вы не можете ссылаться на ?x в Exp, который использует шаблон haskell, вы можете ссылаться на определение в другом модуле, например:

reserved_prefix_x = ?x

Ниже приведен некоторый код, который генерирует переменные, подобные приведенным выше, при одном запуске ghc, а при втором запуске ghc переменные фактически ссылаются на неявные параметры.

{-# 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)))

Файл Main.hs, использующий квазицитатор GenMod.hs:

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

#ifndef stage1
import GEN
#endif

f_ = [h| q_hithere |]

Вы должны вызвать ghc дважды, например:

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.

Хотя ghc дает сбой, он по-прежнему генерирует файл GEN.hs, который содержит:

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

Который будет там, когда вы загрузите Main (без флага -D)

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

Такие неприятности, наверное, того не стоят. Возможно, другие ситуации вызова других программ из TH более мотивируют, например, встроенные вызовы на других языках http://hpaste.org/50837 (пример gfortran)

Поскольку я использовал синтаксический анализатор haskell-src-meta по умолчанию, квазицитата может использовать переменные «reserved_prefix_x», а не «?x». Должна быть возможность принять "?x" без особого труда.

person aavogt    schedule 11.01.2013