问题 我可以使用模板haskell来定义缺失的函数吗?


我有一种情况需要在不同的机器上编译一些Haskell代码。 这些机器中至少有一台有相当旧的版本 Control.Concurrent.STM,那不知道 modifyTVar。我目前的解决方法是从a复制modifyTVar的代码 较新的版本 的包。这让我想知道,如果有可能使用模板Haskell来检查函数是否已经定义并且只定义它,如果它丢失了。我知道正确的解决方案可能是获得更新的软件包,但这种情况让我很好奇。


6465
2018-06-15 19:33


起源

为什么不直接使用CPP(检查版本 stm 包)? - Ganesh Sittampalam
我不知道这可能是一个选择。我试过用 #ifdef,但有点失败了。鉴于我对该主题的了解很少,我也不确定如何用它检查包版本。 - Jakob Runge
我在细节上添加了答案 - Ganesh Sittampalam


答案:


似乎有可能如下。首先是一个辅助模块:

{-# LANGUAGE TemplateHaskell #-}

module AddFn where

import Language.Haskell.TH

-- | Add a function if it doesn't exist.
addFn :: String -> Q [Dec] -> Q [Dec]
addFn name decl = do
    r <- lookupValueName name
    case r of
        Just l -> return []
        Nothing -> report False ("adding missing " ++ name) >> decl

并使用它作为

{-# LANGUAGE TemplateHaskell #-}

module Main where

import AddFn
import qualified Data.Traversable as T

$(addFn "mapM"
    [d| mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
        mapM = T.mapM
    |])

$(addFn "mapM1"
    [d| mapM1 :: (Monad m) => (a -> m b) -> [a] -> m [b]
        mapM1 = T.mapM
    |])

缺点是它正在使用 lookupValueName,这只是在TH的最新版本中,因此在处理旧安装时,这可能无济于事。或许可能的解决方案是打电话 reify 在给定的名称和使用 recover 处理名称缺失时的情况。

更新: 该版本使用 reify 代替 lookupValueName 作品:

-- | Add a function if it doesn't exist.
addFn :: String -> Q [Dec] -> Q [Dec]
addFn name decl = recover decl (reify (mkName name) >> return [])

8
2018-06-15 21:25





模板Haskell有点矫枉过正 - 你可以使用 CPP 相反,使用 MIN_VERSION Cabal将定义的宏:

{-# LANGUAGE CPP #-}

#if MIN_VERSION_stm(2, 3, 0)
-- nothing
#else
modifyTVar = ...
#endif

3
2018-06-16 07:21



我现在已经开始运行,但是我必须进行两次小的调整,以使它与ghci一起运行良好。有必要让ghci包含cabal_macros.h。为此,我按照说明进行操作 stackoverflow.com/questions/19622537/... 和 stackoverflow.com/questions/3388261/ghci-configuration-file - Jakob Runge
最近的电缆,我认为你可以使用 cabal repl - Ganesh Sittampalam
谢谢,我没有意识到这一点。 - Jakob Runge