Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic module name

Tags:

module

haskell

I want to do something like this in Haskell, but the compiler is not letting me.

Is there any way to accomplish this task?

-- 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]
like image 877
Dfr Avatar asked Dec 14 '11 17:12

Dfr


People also ask

What is dynamic module in Nestjs?

A dynamic module is nothing more than a module created at run-time, with the same exact properties as a static module, plus one additional property called module .

What is forRoot Nestjs?

The forRoot sets up the loading of the . env file and the forChild uses it in another module. The problem is that forChild is called before forRoot . The ConfigService would be injected with missing config because forRoot hasn't executed first. > AppModule > ConfigModule.

How do you import modules in Python?

Importing Modules To make use of the functions in a module, you'll need to import the module with an import statement. An import statement is made up of the import keyword along with the name of the module. In a Python file, this will be declared at the top of the code, under any shebang lines or general comments.


2 Answers

Modules in Haskell are not even remotely first-class entities in the ways this would require, I'm afraid.

However, as bzn commented, Template Haskell can be used for problems like this. The result can be a bit clumsy, but if you really need some quick metaprogramming hacks it's not a bad choice. I'm not really an expert with TH, but what you want is pretty simple, with one catch: Neither "ambiguous identifiers" nor "module names" can be captured or quoted in any way, as far as I know, so you'll have to put them in strings given as arguments to the TH function.

Here's a quick and dirty, minimal example:

{-# 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

You phrased things as "running the function" which sounds more like doing a bunch of IO actions, not just collecting a list of stuff, so I added a variant that also sequences the result.

Note that, despite the use of strings here, this is still statically typed--if the qualified names don't exist, or the types don't match up, you'll get the expected compile-time error just as if you'd written everything out by hand.

Here's a quick example of using it. Given the following:

{-# 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)

Assuming the other modules are there and define test, then in GHCi we can see:

> tests
Test 1
Test 2
Count: 2
like image 126
C. A. McCann Avatar answered Nov 12 '22 15:11

C. A. McCann


I don't think you can quote a qualified name prefix like that in template haskell, and the hello identifier isn't in scope, so you might have to fall back to programming with strings.

module ModuleParamsTH where
import Language.Haskell.TH

getAll :: String -> [String] -> ExpQ
getAll valueName moduleNames = 
  listE $ map (varE . mkName . (++ suffix)) moduleNames
  where suffix = "." ++ valueName

which can then be used like so,

{-# LANGUAGE TemplateHaskell #-}
import ModuleParamsTH
import qualified ModuleParamsM1 as M1
import qualified ModuleParamsM2 as M2

runmodules = $(getAll "hello" ["M1", "M2"])

However, I would not do all this. You could just write [M1.hello, M2.hello] or use a type class to abstract over implementations.

like image 26
Anthony Avatar answered Nov 12 '22 16:11

Anthony