2011-12-14 24 views
7

Quiero hacer algo como esto en Haskell, pero el compilador no me deja.Nombre del módulo dinámico

¿Hay alguna manera de lograr esta tarea?

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

Cuál es el problema que desea resolver? Como el nombre de la función es conocido (hello), ¿por qué no simplemente pone las funciones en una lista '[M1.hello, M2.hello]' y hace algo con esa lista? Supongo que de alguna manera podrías salir adelante con TemplateHaskell, pero podría ser demasiado complicado. – bzn

Respuesta

5

no creo que se puede citar un nombre de prefijo calificado así en Haskell plantilla, y el identificador de hello no es en su alcance, lo que podría tener que recurrir a la programación con cuerdas.

module ModuleParamsTH where 
import Language.Haskell.TH 

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

que luego puede ser utilizada como tal,

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

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

Sin embargo, yo no lo haría todo esto. Simplemente escriba [M1.hello, M2.hello] o use una clase de tipo para abstraer las implementaciones.

3

Los módulos no son valores en Haskell. Por lo tanto, eso no es posible. ¿Qué quieres lograr?

8

Los módulos en Haskell no son ni remotamente entidades de primera clase en la forma en que esto requeriría, me temo.

Sin embargo, como comentó bzn, Template Haskell se puede utilizar para problemas como este. El resultado puede ser un poco torpe, pero si realmente necesitas algunos hacks de metaprogramación rápidos, no es una mala elección. No soy realmente un experto con TH, pero lo que quieres es bastante simple, con una sola pega: ni los "identificadores ambiguos" ni los "nombres de módulos" pueden ser capturados o citados de ninguna manera, por lo que yo sé, entonces tú " Tendré que ponerlos en cadenas dadas como argumentos para la función TH.

Aquí está un rápido y sucio, ejemplo mínimo:

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

Se formul cosas como "ejecutar la función" que suena más como hacer un montón de IO acciones, no sólo elaborando una lista de cosas, por lo que agregó una variante que también secuencia el resultado.

Tenga en cuenta que, a pesar del uso de cadenas aquí, este es aún tipado estáticamente: si los nombres calificados no existen, o los tipos no coinciden, obtendrá el tiempo de compilación esperado error como si hubieras escrito todo a mano.

Aquí hay un ejemplo rápido de su uso. Teniendo en cuenta lo siguiente:

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

Suponiendo que los otros módulos están ahí y definen test, a continuación, en GHCi podemos ver:

> tests 
Test 1 
Test 2 
Count: 2 
Cuestiones relacionadas