97

What is the correct way to run an Elixir application?

I'm creating a simple project by:

mix new app

and after that I can do:

mix run

which basically compiles my app once. So when I add:

IO.puts "running"

in lib/app.ex I see "running" only for the first time, each consecutive run does nothing unless there are some changes. What can I do next with generated app.app?

Of course I know I can do:

escript: [main_module: App]

in mix.exs, provide def main(args): and then:

mix escript.build
./app

but's it's kinda cumbersome in my opinion.

There's also something like:

elixir lib/app.exs

but it does not count mix.exs obviously, which is needed for dependencies in my app.

Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
  • 2
    If you want to run an Elixir script (a `.exs` file) but do so in the context of your mix app, you can run `mix run – Michelle Tilley Jun 07 '15 at 06:19

2 Answers2

123

mix run does run your app. It's just that when you simply put IO.puts "something" in a file that line is only evaluated in compile-time, it does nothing at runtime. If you want something to get started when you start your app you need to specify that in your mix.exs.

Usually you want a top-level Application that will get started. To achieve that add a mod option to your mix.exs:

def application do
  [
    # this is the name of any module implementing the Application behaviour
    mod: {NewMix, []},
    applications: [:logger]
  ]
end

And then in that module you need to implement a callback that will be called on application start:

defmodule NewMix do
  use Application

  def start(_type, _args) do
    IO.puts "starting"
    # some more stuff
  end
end

The start callback should actually setup your top-level process or supervision tree root but in this case you will already see that it is called every time you use mix run, although followed by an error.

def start(_type, _args) do
  IO.puts "starting"
  Task.start(fn -> :timer.sleep(1000); IO.puts("done sleeping") end)
end

In this case we are starting a simple process in our callback that just sleeps for one second and then outputs something - this is enough to satisfy the API of the start callback but we don't see "done sleeping". The reason for this is that by default mix run will exit once that callback has finished executing. In order for that not to happen you need to use mix run --no-halt - in this case the VM will not be stopped.

Another useful way of starting your application is iex -S mix - this will behave in a similar way to mix run --no-halt but also open up an iex shell where you can interact with your code and your running application.

Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
  • 6
    Alright, that's almost what I needed! One more thing, can you explain this `returned a bad value: :ok` error when there's not `Task`, or `Agent` or `Supervisor` etc.? How does that work and why do we need a separate process? Why can't I just run some script that executes whatever I need? – Kamil Lelonek Jun 07 '15 at 05:19
  • 21
    An application callback must return a supervision tree. If you don't return one, it will fail. In fact, I would even replace the last expression shown by Paweł by: `Supervisor.start_link [], strategy: :one_for_one`. Returning a task that shuts down may cause the application to fail after sleeping. – José Valim Jun 07 '15 at 08:07
  • 1
    @squixy some of the benefits of using and defining applications are outlined here: http://stackoverflow.com/questions/30422184/where-does-elixir-erlang-fit-into-the-microservices-approach/30423183#30423183 – José Valim Jun 07 '15 at 08:08
  • So the last question is: is it better to use `Task.start(my_other_method)` or run `my_other_method` and then `Supervisor`? – Kamil Lelonek Jun 07 '15 at 08:24
  • 2
    If you're building any kind of long-running application you almost always want a `Supervisor`. I used a `Task` just to have the smallest correct program but as Jose mentioned - you want to start your supervision tree there. – Paweł Obrok Jun 07 '15 at 10:33
  • 1
    Great answer! One question though. You wrote _"It's just that when you just put `IO.puts "something"` in a file that line is only evaluated in compile-time, it does nothing at runtime."_ which seems to corresponds to what I'm seeing but I don't understand the logic of this? Why does it work that way? – user2104969 Jul 28 '16 at 11:58
  • Being able to run Elixir code at compile time allows you to metaprogram easily. The knack is knowing which parts are executed during compilation and which ones make it into the compiled program. – Paweł Obrok Jul 28 '16 at 14:17
  • Ah, right, sounds really interesting. Is there some documentation on how this works? Which parts that are executed during compilation and so on? I tried googling but I didn't find anything that talked specifically about that. – user2104969 Aug 03 '16 at 06:25
  • You might try to read up on macros and code generation - that should give you a better intuition of what is happening when. You might also look into how Elixir implements unicode handling: https://github.com/elixir-lang/elixir/blob/master/lib/elixir/unicode/unicode.ex - it's a great practical example. – Paweł Obrok Aug 03 '16 at 16:25
14

You can run tasks by importing Mix.Task into your module instead of mix run.

I think this is what you're looking for.

On top of that, instead of mix <task.run>, you can simply run mix to run the default task. Simply add default_task: "bot.run" into the list of def project do [..] end in mix.exs. Refer here.

Community
  • 1
  • 1
holyxiaoxin
  • 690
  • 1
  • 7
  • 26