Elixir v1.18.0-rc.0 released

Charlists as sigils - formats charlists as ~c sigils, for example 'foo' becomes ~c"foo"

This is already the case with Elixir 1.17, and IIRC it’s the case since that sigil was introduced.

So does it mean that now the command will do so only if the option is provided?

2 Likes

Yes! So all AST rewriting happens inside —migrate now.

5 Likes

Wow! What a release!

So what is the recommended way for library maintainers regarding JSON? Just depend on 1.18 and migrate to JSON once it is out?

3 Likes

You are helping this community forward in an incredible way. Keep that in mind when problems are hard, or energy is low. There are Elixir devs all around the world having that “hooting and hollering” experience. So thank you for your generous contributions! :pray:

6 Likes

Congrats :slight_smile:

1 Like

there isn’t anyone here by accident who is using nix and has a flake or something with this rc?

1 Like

I ran tests on my repos against 1.18 and everything looks good. The only thing I’m not quite sure about is what to do about intentionally incorrect code in tests?

Example from ExUnit docs
ExUnit.start()

defmodule MyTest do
  use ExUnit.Case, async: true

  test "foo" do
    assert_raise ArithmeticError, fn ->
      1 + "test"
    end
  end
end


elixir ex_unit.exs
    warning: evaluation of operator '+'/2 will fail with a 'badarith' exception
    │
  9 │       1 + "test"
    │         ~
    │
    └─ ex_unit.exs:9:9

    warning: incompatible types given to Kernel.+/2:

        1 + "test"

    given types:

        integer(), binary()

    but expected one of:

        #1
        integer(), integer()

        #2
        integer(), float()

        #3
        float(), integer()

        #4
        float(), float()

    typing violation found at:
    │
  9 │       1 + "test"
    │         ~
    │
    └─ ex_unit.exs:9:9: MyTest."test foo"/1

Running ExUnit with seed: 982904, max_cases: 16

.
Finished in 0.03 seconds (0.03s on load, 0.00s async, 0.00s sync)
1 test, 0 failures

Any recommendation I missed?

2 Likes

This is one of those “whole classes of problems” static typing saves your from having to test. You can just delete the test.

3 Likes

Hmm, in this particular case I’m testing a logger handler so it makes sense to intentionally create a problem. But I guess I can always just throw an exception instead. I wonder if there are cases where we have to write code that produces warnings to test something. Curious to see how it plays out!

4 Likes

The trick we are using in Elixir test suite under similar situations (when we need to force the code to crash to test error handlers and similar) is to hide the value under Process.get(:unused_key, value). For example:

Process.get(:unused_key, 1) + "test"

The reason this works for now is because Process.get/2 will always return a dynamic value, so we cannot assume an error will happen. Maybe we will detect these cases in the future too… then you can ping us again. :smiley: In any case, as the type system becomes smarter, it will be harder to hide compile time errors from it.

12 Likes

Anyone that uses mise (ex-RTX) to manage versions can install Elixir 1.18.0-rc.0? It gets listed but when installing throws a mise ERROR 1.18.0-rc.0 not found in mise tool registry.

Try mise install elixir@1.18-otp-27

Roughly:

{
  description = "Dev environment for Elixir/Nodejs";

  inputs = {
    nixpkgs = { url = "github:NixOS/nixpkgs/nixos-unstable"; };
    flake-utils = { url = "github:numtide/flake-utils"; };
  };

  outputs = { self, nixpkgs, flake-utils }:
   flake-utils.lib.eachDefaultSystem (system:
      let
        inherit (pkgs.lib) optional optionals;
        pkgs = import nixpkgs { inherit system; };

        myPkgs = {
          erlang = {
            defaultPackage = pkgs.erlang_27;
            useOverride = false;
            overrideVersion = "27.0.0";
            overrideSha256 = "cuFeHwyAyS8R7rXcJJxzE7LTZGgDr4x3qMZx63PIowm=";
          };
          elixir = {
            defaultPackage = beamPkg.elixir_1_17;
            useOverride = true;
            overrideVersion = "1.18.0-rc.0";
            overrideSha256 = "OvUL/48gQ4a5TPy1l76+HBF2PoEDAHT9+MC8XNdrSmI=";
          };
        };

        erlang =
          if myPkgs.erlang.useOverride
          then
            pkgs.erlangR25.override
              {
                version = myPkgs.erlang.overrideVersion;
                sha256 = myPkgs.erlang.overriderSha256;
              }
          else myPkgs.erlang.defaultPackage;

        beamPkg = pkgs.beam.packagesWith erlang;

        elixir =
          if myPkgs.elixir.useOverride
          then
            beamPkg.elixir.override
              {
                version = myPkgs.elixir.overrideVersion;
                sha256 = myPkgs.elixir.overrideSha256;
              }
          else myPkgs.elixir.defaultPackage;
      in
      with pkgs;
      {
        devShell = pkgs.mkShell {
          buildInputs = [
            elixir
            glibcLocales
            nodejs_22
          ] ++ optional stdenv.isLinux inotify-tools
            ++ optional stdenv.isDarwin terminal-notifier
            ++ optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [
              CoreFoundation
              CoreServices
            ]);
        };
      });
}
4 Likes

Hey since we’re talking about this, I’m having a similar problem to @martosaur in this doctest:

I tried this:

iex> Process.get(:not_a_key, a < b)

But I still got warning: comparison with structs found:. Anyone have any thoughts?

I can always change the part that warns to prose if there isn’t a workaround.

I have not been following the whole thread but when you call compare?(a < b, Date), is compare?/2 a macro?

If not then it’s first argument is a < b which is comparing structs, whatever you do with the result afterwards.

Same with Process.get(:not_a_key, a < b) but as José said you want the operator out of the call, like this: Process.get(:not_a_key, a) < b.

1 Like

is compare?/2 a macro?

Yep! Its purpose is so that you can write:

compare?(a < b, Date)

And it gets translated to the correct:

Date.compare(a, b) == :lt

You can read more here: CompareChain - Semantic, chained comparisons for Elixir

but as José said you want the operator out of the call, like this: Process.get(:not_a_key, a) < b.

Thanks, I was skimming and missed this point. But it didn’t work either :frowning:

3 Likes

I think you need to do the Process.get trick for both values: Process.get(:not_a_key, a) < Process.get(:not_a_key, b).

1 Like

This worked, thank you!

I think over time the introduction of a smarter compiler could warrant the introduction of tools to work with it that make their purpose clear. For example:

Kernel.dynamic_value/1

# takes anything, returns it as a dynamic
dynamic_value(1) + "test"

Kernel.unreachable!/1

case something do
  :foo -> 10
  :bar ->
    # somehow tells the type checker not to warn on this clause?
    unreachable!("It should not be possible for `something` to be `:bar` at this point.")
end
2 Likes

In this case, you should just remove the a < b from the doctest by removing iex> and have it written as non-executable code. I think using Process.get in the doctest would detract from its main purpose, which is documentation.

That’s the plan in the long term, but something like dynamic_value can also be used to hide actual bugs, so we want to tread carefully.

4 Likes