У меня есть ситуация, когда мне нужно скомпилировать код Haskell на разных машинах.
По крайней мере, одна из этих машин имеет довольно старую версию Control.Concurrent.STM, которая не знает modifyTVar
. Моим текущим решением является копирование кода для modifyTVar из новой версии пакета. Это заставило меня задуматься, можно ли использовать шаблон Haskell, чтобы проверить, определена ли функция и только определить ее, если она отсутствует. Я знаю, что правильное решение, вероятно, было бы для получения более свежих пакетов, но мне стало любопытно.
Могу ли я использовать шаблон haskell для определения недостающих функций?
Ответ 1
Кажется, это возможно следующим образом. Сначала вспомогательный модуль:
{-# 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 [])
Ответ 2
Шаблон Haskell несколько завышен для этого - вы можете использовать CPP
вместо этого, используя макросы MIN_VERSION
, которые Cabal определит:
{-# LANGUAGE CPP #-}
#if MIN_VERSION_stm(2, 3, 0)
-- nothing
#else
modifyTVar = ...
#endif