Today, the only way to use dependencies in Elixir is by first adding them 
to the current Mix project or installing them as archives. This makes 
Elixir less than ideal for simple tasks where you'd usually use a scripting 
language, e.g. download a JSON file, parse it, extract some data. This is 
also adding pressure for a more "batteries-included" approach in the stdlib 
to make these simple tasks more convenient to accomplish and require 
neither creating a Mix project nor installing dependencies.

I'd like to propose adding a `Mix.install` function that will download, 
compile, and load given dependencies into the VM.

Here's how we could use it from an IEx session:

    $ iex
    iex> Decimal.new(42)
    ** (UndefinedFunctionError) function Decimal.new/1 is undefined (module 
Decimal is not available)
        Decimal.new(42)
    iex> Mix.install([{:decimal, "~> 2.0"}])
    :ok
    iex> Decimal.new(42)
    #Decimal<42>

Or from a script:

    $ cat run.exs
    Mix.install([
      {:decimal, "~> 2.0"},
      {:jason, "~> 1.0"}
    ])
    IO.puts Jason.encode!(Decimal.new(42))

    $ elixir run.exs
    "42"

You need to call `Mix.install` in your VM and explicitly list all your 
dependencies. And if you start a new VM, you'd have to call `Mix.install` 
again. 

Under the hood, the `Mix.install` starts Mix and creates a temporary Mix 
project to install and load the dependencies and unloads the temporary 
project when it's done. For simplicity, the initial implementation allows 
you to only call this function outside of a Mix project and you can only 
call it once in a given VM.

The temporary project is preserved across runs so if you start another VM 
and call `Mix.install` with the same set of dependencies, they'll be loaded 
right away without needing to download or compile them. Each set of 
dependencies will have it's own temporary directory, roughly: 
`/tmp/mix_installs/<md5_of_deps>`.

The location and the layout of the temporary project is considered an 
implementation detail that users shouldn't depend on.

The fact that the resulting `mix.lock` file is hidden from the users 
presents challenges around reproducibility and updating deps. If you pin 
your deps to specific versions, a call to `Mix.install` will give 
consistent results, however if you use version requirements like `~> 1.0`, 
it may give different results depending on what the deps resolve to at at 
given time. Similarly, if there's a new version of a package that satisfies 
a given version requirement, `Mix.install` won't automatically install it 
as it already cached the dependency. For this, you'll be able to pass a 
`force: true` flag to bypass the cache and install & compile from scratch 
for a given set of dependencies. This will also help if your install 
somehow got into an inconsistent state.

### Reproducibility

Another way of quickly installing and using dependencies is via workspaces, 
you'd install and automatically load dependencies within a given workspace. 
That brings questions about reproducibility.

Imagine you write a piece of Elixir code that uses dependencies and share 
it with people, how reproducible is it?

- using Mix project, the dependencies are fully reproducible given the Mix 
project maintains a `mix.lock` file

- using `Mix.install`, the dependencies are reproducible within the 
specified version requirements

- using global state like workspaces, the dependencies are not 
reproducible. If you ask a user to run your script, you need to tell them 
what dependencies they need to install

Between global dependencies having very weak reproducibility and Mix 
projects being fully reproducible, `Mix.install` is a compromise where you 
can reasonably reproduce things within a list of requirements. 

### Further reading

Thanks to https://hex.pm/packages/teex authors for exploring some of these 
ideas!

Also see https://github.com/wojtekmach/playground for an earlier 
proof-of-concept and 
https://github.com/elixir-lang/elixir/compare/master...wojtekmach:wm-mix-install
 
for a work-in-progress implementation.

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/ed320491-005c-4414-b1a9-5ca0e540bd3dn%40googlegroups.com.

Reply via email to