If you have an atom in a variable, you can use it as the name of the module.
So you can define c:get_handler/1 as follows:
get_handler("a") -> a; get_handler("b") -> b.
Your c:test/0 looks fine, except you need extra brackets, for example:
test() -> (get_handler("a")):some_function1("here were go for a"), (get_handler("a")):some_function2("aaaa"), (get_handler("b")):some_function1("here we go for b").
Then in modules a and b just define some_function1/1 and some_function/2 , for example:
some_function1(Str) -> io:format("module ~s function some_function1 string ~s~n", [?MODULE, Str]). some_function2(Str) -> io:format("module ~s function some_function2 string ~s~n", [?MODULE, Str]).
Edit: You should also determine the behavior if you are going to do such BTW things, which would mean that you would declare something like this in modules a and b
-behaviour(some_behaviour).
Then create some_behaviour module something like this:
-module(some_behaviour). -callback some_function1 (String :: string()) -> ok . -callback some_function2 (String :: string()) -> ok .
This means that any module, such as a and b , declaring that they support the behavior of some_behaviour , must define these functions, and the compiler will say if they do not. Parameter types and return value are also defined here for static analysis, etc.