Calling imported module functions directly in elixir - import

I have just started learning Elixir and I am unable to figure out how import works in Elixir.
When I import a module into another module using import I am unable to call the imported function by using the module in which it is imported.
But I am able to call function of imported module inside function in the module which it was imported.
defmodule A do
def func do
IO.puts("func called")
end
end
defmodule B do
import A
end
A.func # o/p: "func called"
B.func # (UndefinedFunctionError) undefined function: B.func/0
defmodule B do
import A
def func2 do
func
end
end
B.func2 # o/p "func called"
I am unable to figure out why B.func not works while I was able to call func from func2. Is there some kind of theory that I am missing. Coming from the Ruby background this behaviour looks odd to me. Please can anybody help me out or point me to some good resource to read.

import does not really import anything in the way many other languages do. All it does is makes the imported module's exported functions accessible from the current namespace. To quote the docs
We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name.
If you want A.func and B.func to point to the same function you have a couple options. The first is simple - make a wrapper function:
defmodule B do
def func do
A.func
end
end
If you want some more complex inheritance type stuff you can look into creating a __using__ macro with defoverridable and super

Related

Elixir: Difference between require and import

What is the difference between require and import?
iex> require Integer
Integer
iex> Integer.is_odd(3)
true
and
iex> import List, only: [duplicate: 2]
List
iex> duplicate :ok, 3
[:ok, :ok, :ok]
It seems that they both do the same thing... get macros or functions from other modules.
From the documentation:
We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name.
also
Note that importing a module automatically requires it.
So if you import Integer, you can directly call is_odd, you don't need Integer.is_odd
require
According to this article:
Macro function is being evaluated during compilation. If you want to use it, you need to compile it first. This is exactly what require does.
In the background, it also gives an alias to required module, which means that you can pass as option just like with alias:
require TestModule, as: Test
import
The import directive allows you to skip the module part by importing all or some of the functions/macros:
import IO, only: [puts: 1]
puts "Hello"
As already mentioned, it also calls require in the background to compile it first.

Why can you import a package *after* using its content in a function?

I'm on MATLAB R2014b and have a question that I will illustrate with the following example.
MWE can be made as follows or download it as a .zip file here.
Create a package folder +test on your path with four function files in it:
+test
a.m
b.m
c.m
d.m
Content of a.m:
function a
disp 'Hello World!'
Content of b.m:
function b
a
If you run b from the command line, you will have to import the test package first (import test.*) or run test.b.
Running b will result in an error, since the scope of function b doesn't contain function a. We must import it before it can be used. For this I've created c.m:
function c
import test.*
a
Now running c works fine.
Now my question. If I change c.m to (saved in d.m):
function d
a
import test.*
I.e. the import command is issued after the call to package function a. Running d still works just fine, as if the position of the import command in d.m does not matter. The import appears to have occurred before the call to function a, which in d.m happens on the line before the import.
Why does this happen. Is this the intended behaviour and what are its uses? How and in what order does MATLAB read a .m file and process it? And more off-topic, but in general: how is importing packages handled in different languages compared to MATLAB, does the order of commands matter?
My preemptive conclusion based on the comments: It is probably best practice to only use the import function at or near the beginning of MATLAB code. This makes clearly visible the imported content is available throughout the entire element (e.g. function). It also prevents the incorrect assumption that before the import, the content is not yet available or refers to a different thing with the same name.
MATLAB performs static code analysis prior to evaluating a function in order to determine the variables/functions used by that function. Evaluation of the import statements is part of this static code analysis. This is by design because if you import a package and then use it's functions, MATLAB needs to know this during the static code analysis. As a result, regardless of where you put the import statement within your function, it will have the same effect as if it were at the beginning of the function.
You can easily test this by looking at the output of import which will list all of the current imported packages.
+test/a.m
function a(x)
disp(import)
import test.*
end
test.a()
% test.*
This is why the documentation states to not put an import statement within a conditional.
Do not use import in conditional statements inside a function. MATLAB preprocesses the import statement before evaluating the variables in the conditional statements.
function a(x)
disp(import)
if x
import test.*
else
import othertest.*
end
end
test.a()
% test.*
% othertest.*
The only way to avoid this behavior is to allow the static code analyzer to determine (without a doubt) that an import statement won't be executed. We can do this by having our conditional statement be simply a logical value.
function a()
disp(import)
if true
import test.*
else
import othertest.*
end
end
test.a()
% test.*
As far as importing compared to other languages, it really depends on the language. In Python for example, you must place the import before accessing the module contents. In my experience, this is the typical case but I'm sure there are many exceptions. Every language is going to be different.

Elixir: generating module with proper import

I'm trying to write the macro that generates a module:
defmodule GenModules do
defmacro module(name, do: body) do
quote do
defmodule unquote(String.to_atom(name)) do
unquote(body)
end
end
end
end
What I'm missing is to how to inject the 'import' statement that would refer the module the macro will be called from?
I.e., the following code:
defmodule Test do
import GenModules
module "NewModule" do
def test_call do
hello_world
end
end
def hello_world do
IO.puts "Hello"
end
end
won't compile because hello_world function is not visible from the generated NewModule.
So I need to generate
import Test
before the body of the module, somehow getting the name of the module the macro was called from. How do I do this?
Thank you,
Boris
To get the module your macro was called from you can use the special form __CALLER__. It contains a bunch of information, but you can extract the calling module like so:
__CALLER__.context_modules |> hd
But more generally I don't think what you want is possible - you cannot import a module before you finish defining it. For example:
defmodule A do
defmodule B do
import A
end
end
results in an error:
** (CompileError) test.exs:3: module A is not loaded but was defined.
This happens because you are trying to use a module in the same context it is defined.
Try defining the module outside the context that requires it.

Scala JSR 223 importing types/classes

The following example fails because the definition for Stuff can't be found:
package com.example
import javax.script.ScriptEngineManager
object Driver5 extends App {
case class Stuff(s: String, d: Double)
val e = new ScriptEngineManager().getEngineByName("scala")
println(e.eval("""import Driver5.Stuff; Stuff("Hello", 3.14)"""))
}
I'm unable to find any import statement that allows me to use my own classes inside of the eval statement. Am I doing something wrong? How does one import classes to be used during eval?
EDIT: Clarified example code to elicit more direct answers.
The Scripting engine does not know the context. It surely can't access all the local variables and imports in the script, since they are not available in the classfiles. (Well, variable names may be optionally available as a debug information, but it is virtually impossible to use them for this purpose.)
I am not sure if there is a special API for that. Imports are different across various languages, so bringing an API that should fit them all can be difficult.
You should be able to add the imports to the eval-ed String. I am not sure if there is a better way to do this.

MATLAB: modify import list of other scopes dynamically?

So, is there a way in MATLAB to modify the import list of a scope different from the current one? That is, I would like to be able to do this:
function import_mypackage()
evalin('caller', 'import mypackage.*');
end
This doesn't work. No error is produced when calling import_mypackage, but the namespace contained in mypackage is not imported, i.e:
function foo()
import_mypackage;
g(); % Wanted mypackage.g() but got: Undefined function or variable 'g()'
end
I know that you can modify dynamically the import list of the current scope either using eval or by passing a variable to import(). However, I cannot find any way to modify the import list of other scopes. Is there any way?
EDIT: Rody Oldenhuis found in his answer a strange behavior of function import. He suggested that evalin actually modifies the import list of the caller but that such list is cleared once you return from import_mypackage. However, I think that what is happening is that import expressions always evaluate in the current worspace. See:
function import_mypackage()
evalin('caller', 'L = import(''mypackage.foo'');');
foo; % It works! So the import happened in this workspace
end
Modified from Rody's response:
function foo()
import mypackage.f;
import_mypackage;
L
import
end
will print:
L =
'mypackage.foo'
ans =
'mypackage.f'
indicating the L was set to the import list of import_mypackage scope but that it never really cleared the import list of foo().
EDIT: #RodyOldenhuis
The reason why I want to mess around with the import list of the caller scope is that I want to define an "aliased" version of import(). Such alias_import() would allow the user to define package aliases so that:
alias_import my_toolbox
may be equivalent to:
import my_toolbox_v1.*
or to:
import my_toolbox_v2.*
that is, I want to be able to maintain several versions of a toolbox and control dynamically the version that is being imported. This is useful for comparing results between different my_toolbox versions or whenever you want to ensure that certain my_toolbox version will be used. All without having to go to the code and manually change import directives in hundreds of functions whenever I am upgrading to a new version of my_toolbox. Of course there is the alternative of making this in an indirect way, like:
import(alias_import('my_toolbox'))
so that alias_import will not perform the actual importing but simply will produce the input to the built-in import. This is perfectly fine but a bit more verbose and that is why I would have liked alias_import to modify the caller's import list. But after seeing the weird behavior of evalin() I think I rather leave things like they are now.
It seems you have stumbled upon some odd behavior (I'm not using the workd "bug" until I'm sure this is not what Mathworks intended :).
It seems that
function import_mypackage()
evalin('caller', 'import mypackage.*');
end
function foo()
import_mypackage;
g();
end
does not work, but
function import_mypackage()
evalin('caller', 'L = import mypackage.*');
end
function foo()
import_mypackage;
L
import
end
shows
L =
'mypackage.*'
ans =
Empty cell array: 0-by-1
which implies that the import list in the caller (foo) is cleared when the function import_mypackage goes out of scope.
I'd say this is at least unwanted behaviour, and I'd say this is a case for a bug report.
As a work-around, use something like
function L = import_mypackage()
L = import('mypackage.*');
end
function foo()
L = import_mypackage;
import(L{:});
% do stuff with pacakge contents
...
end
which I think is advisable over evalin anyway.