So I wanted to write a single line script to execute quickly in the console, using Ecto.Query
.
Hence I wanted to write:
MyApp.Repo.update_all(
Ecto.Query.from(d in MyApp.MyModel,
where: is_nil(d.my_attribute)),
set: [my_attribute: "My value"]
)
But I get this error:
** (CompileError) iex:3: you must require Ecto.Query before invoking the macro Ecto.Query.from/2
Can anyone explain this to me being still phoenix noob?
How can I achieve my single line command?
To me using require
or import
has global implications, it adds context and make the subsequent code less predictable, so it is not desired unless within a file (where scope is controlled). Am I being too limited?
Public functions in modules are globally available, but macros are not.
import Ecto.Query, only: [from: 2]
#or
require Ecto.Query
1 Like
Import has lexical implications, not global implications. The distinction is that import
only makes a change within the current lexical scope. You can do it within a function for example and then it only makes the functions / macros available for that function:
def foo() do
import Ecto.Query
from() # works
end
def bar() do
from() # won't work
end
Even if you put require
or import
in a bare ex
or exs
file, the scope is always limited to that file. It is impossible in Elixir to add a global import, you don’t have to worry about polluting global scope.
require
is also lexically scoped, but unlike import
it doesn’t change the context. Macros are a powerful tool, but they can also introduce complexity, so using require
helps indicate that the module being require
d has macros that this module will be using. It also helps the compiler generate the correct compilation graph, since it tells the compiler that it needs to ensure Ecto.Query
is compiled before attempting to compile your module.
There are perfectly good arguments for not using import
if you like keeping your function / macro calls very explicit. require
though is not optional, which is fine since it doesn’t change any of the code that comes afterward anyway.
4 Likes
Thanks for clarification!
Is there a way I could know from
is a macro and not a function? I am still unclear over the difference between the two but I reckon on that, I need to make deeper researches.
A function is simply something that takes an Elixir value, and returns an elixir value. When you have add(num1, num2 + 2)
, whenever add/2
is called it receives whatever value num1
and num2 + 2
to have at the point of evaluation.
A macro is a function that receives code itself as an argument, and is allowed to return code that replaces what was there before. So for example if you have some_macro(a + b)
it doesn’t receive the value of a + b
, but rather a representation of the code a + b
. It’s then allowed to return different code. This process happens not at runtime, but when the context of the some_macro
call is compiled.
This is how, for example, the Ecto.Query macros can prevent SQL injection at compile time. If you try to do something dangerous like: from u in User, where: fragment("name = #{name}")
you get ** (Ecto.Query.CompileError) to prevent SQL injection attacks
. This is because the from
macro receives the actual code we pass to it, and can look through that code for dangerous behavior.
Not by looking at a call. This is part of the purpose of the require Foo
. It helps clue us into the fact that calls involving the Foo
module may involve macros.
3 Likes
So many thanks for those explanations!
Have a great day
1 Like
Thank you. This, at last, clarified why require
is called require
. That was really bugging me