我想在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]
Run Code Online (Sandbox Code Playgroud)
我担心,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
Run Code Online (Sandbox Code Playgroud)
你把事情称为"运行功能",听起来更像是做一堆IO
动作,而不仅仅是收集一些东西,所以我添加了一个同样对结果进行排序的变体.
请注意,尽管在这里使用了字符串,但这仍然是静态类型的 - 如果限定名称不存在,或者类型不匹配,您将获得预期的编译时错误,就像您' d手工写出一切.
这是一个使用它的快速示例.鉴于以下内容:
{-# 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)
Run Code Online (Sandbox Code Playgroud)
假设其他模块在那里并定义test
,那么在GHCi中我们可以看到:
> tests
Test 1
Test 2
Count: 2
Run Code Online (Sandbox Code Playgroud)
我不认为你可以在模板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
Run Code Online (Sandbox Code Playgroud)
哪个可以像这样使用,
{-# LANGUAGE TemplateHaskell #-}
import ModuleParamsTH
import qualified ModuleParamsM1 as M1
import qualified ModuleParamsM2 as M2
runmodules = $(getAll "hello" ["M1", "M2"])
Run Code Online (Sandbox Code Playgroud)
但是,我不会这样做.您可以编写[M1.hello, M2.hello]
或使用类型类来抽象实现.