动态模块名称

时间:2011-12-14 17:12:12

标签: haskell module

我想在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]

3 个答案:

答案 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中的值。因此,这是不可能的。你想要达到什么目标?