mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-11-11 22:51:43 +08:00
Add a notebook about the unique features behind Elixir and Livebook (#314)
This commit is contained in:
parent
91a9f62fb9
commit
fcf53c4bf2
4 changed files with 152 additions and 118 deletions
|
|
@ -55,6 +55,11 @@ defmodule Livebook.Notebook.Explore do
|
||||||
image_url: "/images/logo.png"
|
image_url: "/images/logo.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defnotebook(:elixir_and_livebook,
|
||||||
|
description: "Learn how to use some of Elixir and Livebook unique features together.",
|
||||||
|
image_url: "/images/live-elixir.png"
|
||||||
|
)
|
||||||
|
|
||||||
defnotebook(:intro_to_elixir,
|
defnotebook(:intro_to_elixir,
|
||||||
description: "New to Elixir? Learn about the language and its core concepts.",
|
description: "New to Elixir? Learn about the language and its core concepts.",
|
||||||
image_url: "/images/elixir.png"
|
image_url: "/images/elixir.png"
|
||||||
|
|
@ -90,7 +95,8 @@ defmodule Livebook.Notebook.Explore do
|
||||||
@spec notebook_infos() :: list(notebook_info())
|
@spec notebook_infos() :: list(notebook_info())
|
||||||
def notebook_infos() do
|
def notebook_infos() do
|
||||||
[
|
[
|
||||||
@intro_to_livebook
|
@intro_to_livebook,
|
||||||
|
@elixir_and_livebook
|
||||||
# @intro_to_elixir, @intro_to_nx, @intro_to_axon, @intro_to_vega_lite
|
# @intro_to_elixir, @intro_to_nx, @intro_to_axon, @intro_to_vega_lite
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
138
lib/livebook/notebook/explore/elixir_and_livebook.livemd
Normal file
138
lib/livebook/notebook/explore/elixir_and_livebook.livemd
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
# Elixir and Livebook
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
You can use code cells to execute any Elixir code:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
IO.puts("hello world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
But you can define modules inside cells too!
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Utils do
|
||||||
|
@doc """
|
||||||
|
Generates a random binary id.
|
||||||
|
"""
|
||||||
|
@spec random_id() :: binary()
|
||||||
|
def random_id() do
|
||||||
|
:crypto.strong_rand_bytes(20) |> Base.encode32(case: :lower)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're surprised by the above output, keep in mind that
|
||||||
|
every Elixir expression evaluates to some value and as so
|
||||||
|
does module compilation!
|
||||||
|
|
||||||
|
Having the module defined, let's take it for a spin.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Utils.random_id()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Autocompletion
|
||||||
|
|
||||||
|
Elixir code cells also support autocompletion by pressing
|
||||||
|
<kbd>Ctrl</kbd> + <kbd>Spacebar</kbd>. You can try it out by making sure the
|
||||||
|
module in the previous section has been defined and then
|
||||||
|
put the cursor after the `.` below and press <kbd>Ctrl</kbd> + <kbd>Spacebar</kbd>:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Utils.
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also press `Tab` to cycle across the different options.
|
||||||
|
|
||||||
|
## Imports
|
||||||
|
|
||||||
|
You can import modules as normally to make the imported functions visible
|
||||||
|
to all subsequent cells. Usually you want to keep `import`, `alias`, and
|
||||||
|
`require` in the first section, as part of the notebook setup.
|
||||||
|
|
||||||
|
For instance, you can import `IEx.Helpers` and bring all of the amazing
|
||||||
|
conveniences in Elixir's shell to your notebook:
|
||||||
|
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
import IEx.Helpers
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
h(Enum.map())
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
# Sidenote: http://www.numbat.org.au/thenumbat
|
||||||
|
i("I ❤️ Numbats")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using packages
|
||||||
|
|
||||||
|
Sometimes you need a dependency or two and notebooks are no exception to this.
|
||||||
|
In Livebook, you can use [`Mix.install/2`](https://hexdocs.pm/mix/Mix.html#install/2)
|
||||||
|
to bring dependencies into your notebook! This approach is especially useful when
|
||||||
|
sharing notebooks because everyone will be able to get the same dependencies.
|
||||||
|
Let's try this out:
|
||||||
|
|
||||||
|
**Note:** compiling dependencies may use a reasonable amount of memory. If you are
|
||||||
|
hosting Livebook, make sure you have enough memory allocated to the Livebook
|
||||||
|
instance, otherwise the command below will fail.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Mix.install([
|
||||||
|
{:jason, "~> 1.2"}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
%{elixir: "is awesome"}
|
||||||
|
|> Jason.encode!()
|
||||||
|
|> IO.puts()
|
||||||
|
```
|
||||||
|
|
||||||
|
It is a good idea to specify versions of the installed packages,
|
||||||
|
so that the notebook is easily reproducible later on.
|
||||||
|
|
||||||
|
Also keep in mind that `Mix.install/2` can be called only once
|
||||||
|
per runtime, so if you need to modify the dependencies, you should
|
||||||
|
go to the notebook runtime configuration and **reconnect** the current
|
||||||
|
runtime. Let's learn how to do that.
|
||||||
|
|
||||||
|
## Runtimes
|
||||||
|
|
||||||
|
Livebook has a concept of **runtime**, which in practice is an Elixir node responsible
|
||||||
|
for evaluating your code. You can choose the runtime by clicking the "Runtime" icon
|
||||||
|
on the sidebar (or by using the `rs` keyword shortcut).
|
||||||
|
|
||||||
|
By default, a new Elixir node is started (similarly to starting `iex`). You
|
||||||
|
can click reconnect whenever you want to discard the current node and start
|
||||||
|
a new one.
|
||||||
|
|
||||||
|
You can also choose to run inside a _Mix_ project (as you would with `iex -S mix`),
|
||||||
|
manually _attach_ to an existing distributed node, or run your Elixir notebook
|
||||||
|
_embedded_ within the Livebook source itself.
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
It is also possible to run tests directly from your notebooks.
|
||||||
|
The key is to disable `ExUnit`'s autorun feature and then explicitly
|
||||||
|
run the test suite after all test cases have been defined:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
ExUnit.start(autorun: false)
|
||||||
|
|
||||||
|
defmodule MyTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
test "it works" do
|
||||||
|
assert true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ExUnit.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
This helps you follow best practices and ensure the code you write
|
||||||
|
behaves as expected!
|
||||||
|
|
@ -18,7 +18,7 @@ and run them locally.
|
||||||
|
|
||||||
Each notebook consists of a number of cells, which serve as primary building blocks.
|
Each notebook consists of a number of cells, which serve as primary building blocks.
|
||||||
There are **Markdown** cells (such as this one) that allow you to describe your work
|
There are **Markdown** cells (such as this one) that allow you to describe your work
|
||||||
and **Elixir** cells where the magic takes place!
|
and **Elixir** cells to run your code!
|
||||||
|
|
||||||
To insert a new cell move your cursor between cells and click one of the revealed buttons. 👇
|
To insert a new cell move your cursor between cells and click one of the revealed buttons. 👇
|
||||||
|
|
||||||
|
|
@ -72,119 +72,6 @@ with just a few assumptions on how particular elements are represented. Thanks t
|
||||||
approach you can easily keep notebooks under version control and get readable diffs.
|
approach you can easily keep notebooks under version control and get readable diffs.
|
||||||
You can also easily preview those files, reuse for blog posts, and even edit in a text editor.
|
You can also easily preview those files, reuse for blog posts, and even edit in a text editor.
|
||||||
|
|
||||||
## Modules
|
|
||||||
|
|
||||||
As we have seen, Elixir cells can be used for working on tiny snippets,
|
|
||||||
but you may as well define a module!
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
defmodule Utils do
|
|
||||||
@doc """
|
|
||||||
Generates a random binary id.
|
|
||||||
"""
|
|
||||||
@spec random_id() :: binary()
|
|
||||||
def random_id() do
|
|
||||||
:crypto.strong_rand_bytes(20) |> Base.encode32(case: :lower)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're surprised by the above output, keep in mind that
|
|
||||||
every Elixir expression evaluates to some value and as so does module compilation!
|
|
||||||
|
|
||||||
Having the module defined, let's take it for a spin.
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
Utils.random_id()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Imports
|
|
||||||
|
|
||||||
You can import modules as normally to make the imported functions visible
|
|
||||||
to all subsequent cells. Usually you want to keep `import`, `alias` and `require`
|
|
||||||
in the first section, as part of the notebook setup.
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
import IEx.Helpers
|
|
||||||
```
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
h(Enum.map())
|
|
||||||
```
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# Sidenote: http://www.numbat.org.au/thenumbat
|
|
||||||
i("I ❤️ Numbats")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Runtimes
|
|
||||||
|
|
||||||
Livebook has a concept of **runtime**, which in practice is an Elixir node responsible
|
|
||||||
for evaluating your code.
|
|
||||||
|
|
||||||
By default, a new Elixir node is started (similarly to starting `iex`),
|
|
||||||
but you can also choose to run inside a Mix project (as you would with `iex -S mix`)
|
|
||||||
or even manually attach to an existing distributed node!
|
|
||||||
You can configure the runtime by clicking the "Runtime" icon on the sidebar.
|
|
||||||
|
|
||||||
## Using packages
|
|
||||||
|
|
||||||
Sometimes you need a dependency or two and notebooks are no exception to this.
|
|
||||||
|
|
||||||
One way to work with packages is to create a Mix project and configure the notebook
|
|
||||||
to run in its context (as pointed out above). This approach makes sense if you already have
|
|
||||||
a Mix project that you are working on, especially because this makes all project's
|
|
||||||
modules available as well.
|
|
||||||
|
|
||||||
But there are cases when you just want to play around with a new package
|
|
||||||
or quickly prototype some code that relies on such. Fortunately, Elixir v1.12+ ships with
|
|
||||||
[`Mix.install/2`](https://hexdocs.pm/mix/Mix.html#install/2) that allows you to install
|
|
||||||
dependencies into your Elixir runtime! This approach is especially useful when sharing notebooks
|
|
||||||
because everyone will be able to get the same dependencies. Let's try this out:
|
|
||||||
|
|
||||||
**Note:** compiling dependencies may use a reasonable amount of memory. If you are
|
|
||||||
hosting Livebook, make sure you have enough memory allocated to the Livebook
|
|
||||||
instance, otherwise the command below will fail.
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
Mix.install([
|
|
||||||
{:jason, "~> 1.2"}
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
%{elixir: "is awesome"}
|
|
||||||
|> Jason.encode!()
|
|
||||||
|> IO.puts()
|
|
||||||
```
|
|
||||||
|
|
||||||
It is a good idea to specify versions of the installed packages,
|
|
||||||
so that the notebook is easily reproducible later on.
|
|
||||||
|
|
||||||
Also keep in mind that `Mix.install/2` can be called only once
|
|
||||||
per runtime, so if you need to modify the dependencies, you should
|
|
||||||
go to the notebook runtime configuration and **reconnect** the current runtime.
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
It is also possible to run tests directly from your notebooks.
|
|
||||||
The key is to disable `ExUnit`'s autorun feature and then explicitly
|
|
||||||
run the test suite after all test cases have been defined:
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
ExUnit.start(autorun: false)
|
|
||||||
|
|
||||||
defmodule MyTest do
|
|
||||||
use ExUnit.Case, async: true
|
|
||||||
|
|
||||||
test "it works" do
|
|
||||||
assert true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ExUnit.run()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Math
|
## Math
|
||||||
|
|
||||||
Livebook uses $\\TeX$ syntax for math.
|
Livebook uses $\\TeX$ syntax for math.
|
||||||
|
|
@ -204,10 +91,13 @@ to optimise how you move around. Livebook leverages the concept of
|
||||||
Make sure to check out the shortcuts by clicking the "Keyboard" icon in
|
Make sure to check out the shortcuts by clicking the "Keyboard" icon in
|
||||||
the sidebar or by typing `?`.
|
the sidebar or by typing `?`.
|
||||||
|
|
||||||
## Final notes
|
## Next steps
|
||||||
|
|
||||||
Livebook is an open source project, so feel free to look into
|
That's our quick your intro to Livebook! Now you are ready to learn
|
||||||
[the repository](https://github.com/elixir-nx/livebook)
|
how Elixir integrates with Livebook in the ["Elixir and Livebook"](/explore/notebooks/elixir-and-livebook) notebook.
|
||||||
|
|
||||||
|
Finally, remember Livebook is an open source project, so feel free to look
|
||||||
|
into [the repository](https://github.com/elixir-nx/livebook)
|
||||||
to contribute, report bugs, suggest features or just skim over the codebase.
|
to contribute, report bugs, suggest features or just skim over the codebase.
|
||||||
|
|
||||||
Now go ahead and build something cool! 🚢
|
Now go ahead and build something cool! 🚢
|
||||||
|
|
|
||||||
BIN
priv/static/images/live-elixir.png
Normal file
BIN
priv/static/images/live-elixir.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Loading…
Add table
Reference in a new issue