11

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

4

2 回答 2

8

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

{-# 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 [])
于 2014-06-15T21:25:07.267 回答
3

模板 Haskell 对此有点矫枉过正 - 您可以使用Cabal 将定义CPP的宏来代替:MIN_VERSION

{-# LANGUAGE CPP #-}

#if MIN_VERSION_stm(2, 3, 0)
-- nothing
#else
modifyTVar = ...
#endif
于 2014-06-16T07:21:22.660 回答