我想在Haskell中做这样的事情,但编译器不让我这么做。
有没有办法完成这项任务?
-- both modules export function named "hello"
-- and I want to run it in every module
import qualified MyMod as M1
import qualified MyAnotherMod as M2
runmodules = map (\m -> m.hello) [M1, M2]
答案 0 :(得分:8)
Haskell中的模块甚至不是以这种方式远程一流的实体,我担心。
然而,正如bzn评论的那样,Template Haskell可用于此类问题。结果可能有点笨拙,但如果你真的需要一些快速的元编程黑客,这不是一个糟糕的选择。我不是TH的专家,但是你想要的只是一个问题:根据我所知,“模糊标识符”或“模块名称”都不能以任何方式被捕获或引用,所以你要'我必须将它们放在作为TH函数的参数给出的字符串中。
这是一个快速而肮脏,极少的例子:
{-# LANGUAGE TemplateHaskell #-}
module MapModuleTH where
import Language.Haskell.TH
mapQual :: [String] -> String -> ExpQ
mapQual ms n = listE $ map (\m -> varE . mkName $ m ++ "." ++ n) ms
mapMQual :: [String] -> String -> ExpQ
mapMQual ms n = appE (varE 'sequence) $ listE $ map (\m -> varE . mkName $ m ++ "." ++ n) ms
你把事情称为“运行功能”,这听起来更像是做了一堆IO
动作,而不只是收集一些东西,所以我添加了一个同样对结果进行排序的变体。
请注意,尽管此处使用了字符串,但 仍然是静态类型的 - 如果限定名称不存在,或者类型不匹配,您将获得预期的编译时错误,就像你手动写完所有内容一样。
以下是使用它的快速示例。鉴于以下内容:
{-# LANGUAGE TemplateHaskell #-}
module MapModule where
import MapModuleTH
import qualified Test1 as T1
import qualified Test2 as T2
tests = do xs <- $(mapMQual ["T1", "T2"] "test")
putStrLn $ "Count: " ++ show (length xs)
假设其他模块在那里并定义test
,那么在GHCi中我们可以看到:
> tests
Test 1
Test 2
Count: 2
答案 1 :(得分:5)
我认为你不能在模板haskell中引用一个限定名称前缀,并且hello
标识符不在范围内,所以你可能不得不回退到使用字符串编程。
module ModuleParamsTH where
import Language.Haskell.TH
getAll :: String -> [String] -> ExpQ
getAll valueName moduleNames =
listE $ map (varE . mkName . (++ suffix)) moduleNames
where suffix = "." ++ valueName
然后可以这样使用,
{-# LANGUAGE TemplateHaskell #-}
import ModuleParamsTH
import qualified ModuleParamsM1 as M1
import qualified ModuleParamsM2 as M2
runmodules = $(getAll "hello" ["M1", "M2"])
但是,我不会这样做。您可以编写[M1.hello, M2.hello]
或使用类型类来抽象实现。
答案 2 :(得分:3)
模块不是Haskell中的值。因此,这是不可能的。你想要达到什么目标?