Elixir v1.18.0 released

Congrats on the new release! I’m really excited to get using it.

But I have a question about the new :auto_reload IEx option.

I see that, with this new option enabled, IEx now automatically picks up any changes to my code when it gets recompiled. But the code still doesn’t get recompiled automatically. I have to e.g. do mix compile in another terminal window, then the newly-compiled version will be available in IEx.

But this isn’t really an improvement over just typing recompile() within IEx itself. Is there some way to watch the files and auto-recompile them when they change, so I don’t have to make the manual recompilation step at all?

I know that Phoenix recompiles code in dev mode with Plug.CodeReloader, but this only happens on the next request, i.e. not at the actual point when the files change. So I’d still have to have my Phoenix server running, then go and e.g. refresh a page in the browser after changing code before the changes will be available in IEx. That’s not a good workflow if I want to only be flipping between IEx and my editor.

Is there an easier way to use auto_reload: true? Am I misunderstanding anything?

1 Like

what joke? I do not type type it was a joke!

1 Like

Someone please correct me if I’m wrong but I believe the use case here is that your language server, if using the same version of Elixir going forward, is running and compiles your application then it would serve that purpose for you.

Whether the LSPs do that yet with 1.18, I’m not sure but I believe that is the idea going forward

But yes, if you were editing without an LSP running, and no new request came in, then it would not be recompiled. You’d need some other OS process to watch and manually recompile on save.
The LSP approach is likely your best option though, since presumably you’d be in your editor anyway

2 Likes

I haven’t tried 1.18 yet (waiting on Nixpkgs release, which should be imminent) but I would imagine that the following would do exactly what you want:

  • in one terminal: git ls-files | entr mix compile
  • run IEx in another terminal

(This particular example uses entr; here’s a nice writeup about it.)

4 Likes

On the topic of the Nixpkgs release, there’s a discussion going on about what the policy should be for how to pair Erlang/OTP versions with Elixir (and other BEAM lang) updates.

My position is that Elixir should use the latest stable Erlang/OTP release at the time of release. (This is just the default, you can of course pick a different version if you’re inclined, it’s just extra work.) If anyone else has feedback please chime in here: elixir_1_18: init at 1.18.0 by ankhers · Pull Request #366645 · NixOS/nixpkgs · GitHub

2 Likes

Type type I typed it was a joke. What was I typing? I have a lot type learn.

1 Like

To confirm what others have already said, you can use fsevents or similar to watch your lib and run mix compile. Or in the future, when LSP is using the listeners too, they will automatically recompile, and then they will appear automatically in IEx. :slight_smile:

FWIW, that’s precisely why the option is named auto_reload and not auto_recompile!

10 Likes

Type you :pray:

Thanks for clarifying, everybody.

Based on these responses, I wrote up a short guide to the new :auto_reload feature. I hope people like it, and please tell me if I got anything wrong!

5 Likes

In case you want to go even deeper, you can note that previously, if you did this:

  1. Started iex -S mix
  2. Changed a file
  3. Called mix compile
  4. Called recompile() in IEx

recompile() would not pick up the new module at all, because it had already been compiled. Elixir v1.18 makes it so recompile() works consistently AND adds the auto_reload option. Overall, it is all about playing better with multiple OS processes. :slight_smile:

10 Likes

Whaaaaat?? This explains some very rare absolute headscratchers where I have had to nuke the LS and build and go make ritual sacrifices to the old gods in the woods before it all just start magically working again.

Usually when rapidly prototyping new schema and just going ??? on me :sob: haven’t really thought about restarting iex since recompile returns :noop :joy:

5 Likes

The following test was failed only once while I tested deterministic builds:

  1) test Unix cmd/3 by line (SystemTest)
     test/elixir/system_test.exs:196
     match (=) failed
     code:  assert {["hello", "world"], 0} = System.cmd("echo", ["-n", "hello\nworld"], into: [], lines: 3)
     left:  {["hello", "world"], 0}
     right: {["hello"], 0}
     stacktrace:
       test/elixir/system_test.exs:200: (test)

That would not explain that because LS uses a separate _build. Plus, in all cases, simply restarting the server should have been enough.

1 Like

This indeed explains a problem that has puzzled me for a long time.

In Emacs, I use an external system process (which will call mix format) to format Elixir code. After each formatting is completed, and I input recompile() in IEx, I find that the execution result of the code is not updated. I have to reopen IEx.

Today, I tried v1.18 with :auto_reload, and the problem is finally solved. Thanks!


Also, another question: why isn’t :auto_reload set to true by default? Or is there a plan to make it true by default?

3 Likes

Same.

I had a keyboard shortcut for mix format and I’ve noticed this.

3 Likes

I enjoyed the new ExUnit’s parameterize feature. Thanks! :clap:
It helped to refactor tests and increase coverage.

I also found a fun hack to avoid older Elixir versions’ CI test failures. Maybe it will be useful for some of you :slight_smile:

  use ExUnit.Case,
    parameterize: [
      %{some_option: 1},
      %{some_option: 2},
      %{some_option: 3}
    ]

  setup_all do
    if Version.match?(System.version(), ">= 1.18.0") do
      []
    else
      # One of the `parameterize` options for older Elixir versions
      [some_option: 1]
    end
  end
2 Likes

The problem you described is solved without auto_reload: true, using recompile() now will work as it will see the changes to disk (it did not before, which is why you were puzzled). auto_reload just makes it so applies immediately.

We may make it the default in the future, we are trialing it, but changing code live could also bring its own issues. Should a change in the editor cause a long running script in IEx to now run a new code? :slight_smile:

7 Likes

Thank you for explaining this issue again. Otherwise, I would have misunderstood the solution.


(I have to admit, my question at that time was just something I asked offhand. But your question has sparked some thoughts in me.)

Maybe, we shouldn’t.

If we step out of the context of Elixir and imagine a typical OS process, there are a few ways I know to update its code: completely restarting the process, hot-reloading a dynamic library, or some other methods I don’t know. However, regardless of the method, there is always a clear step that explicitly requires the OS process code to be updated.

If auto_reload: true is set as the default behavior, the code running within a process implicitly changes along with the source code. That’s not good, generally.

And, let developers explicitly set IEx.configure(auto_reload: true) themselves, so that when they encounter some unexpected behavior, they can consciously consider the underlying cause about :auto_reloead.

Also, I can think of a quote that might fit here as well - “Explicit better than implicit”.

So, I think current default is good. Don’t change it. :wink:

4 Likes

v1.18.1 is out addressing the feedback received so far: Release v1.18.1 · elixir-lang/elixir · GitHub


1. Enhancements

  • [Kernel] Do not emit type violation warnings when comparing or matching against literals
  • [Kernel] Do not validate clauses of private overridable functions

2. Bug fixes

Elixir

  • [Code.Fragment] Ensure Code.Fragment.container_cursor_to_quoted/2 with :trailing_fragment parses expressions that were supported in previous versions
  • [Kernel] Do not crash when typing violation is detected on dynamic dispatch
  • [Kernel] Properly annotate the source for warnings emitted by the compiler with the @file annotation
  • [Kernel] Properly annotate the source for warnings emitted by the type system with the @file annotation
  • [Kernel] Remove :no_parens metadata when using capture with arity on all cases
  • [Kernel] Ensure diagnostic traces are kept backwards compatible

ExUnit

  • [ExUnit.Case] Ensure async groups do not run concurrenly while the test suite is still loading
  • [ExUnit.Case] Ensure --repeat-until-failure can be combined with groups

Mix

  • [mix compile.elixir] Store compilation results if compilation fails due to --warnings-as-errors
  • [mix deps.loadpaths] Add build lock
  • [mix escript.build] Ensure build succeeds when protocol consolidation is disabled
  • [Mix.Shell] Ensure encoding is properly respected on Windows and Unix systems
12 Likes

Hi, I’ve just upgraded to 1.18.0 and subsequently 1.18.1 and find that my tests won’t run in either. I get the message “cannot add module named *** to test suite after the suite starts running”. Looking at the ExUnit source code this was added as part of the work for Parameterized tests for 1.18.x

Changing

use ExUnit.Case, async:

to

use ExUnit Case

fixes the issue, but is it expected that tests which ran fine in 1.17.x should no longer run?