Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce an Explore section #310

Merged
merged 10 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1><img src="https://github.com/elixir-nx/livebook/raw/main/priv/static/logo-with-text.png" alt="Livebook" width="400"></h1>
<h1><img src="https://github.com/elixir-nx/livebook/raw/main/priv/static/images/logo-with-text.png" alt="Livebook" width="400"></h1>

Livebook is a web application for writing interactive and collaborative code notebooks. It features:

Expand Down
84 changes: 84 additions & 0 deletions lib/livebook/notebook/explore.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
defmodule Livebook.Notebook.Explore.Utils do
@moduledoc false

@doc """
Defines a module attribute `attr` with notebook
parsed from the corresponding file.
"""
defmacro defnotebook(attr) do
quote bind_quoted: [attr: attr] do
filename = to_string(attr) <> ".livemd"
path = Path.join([__DIR__, "explore", filename])

notebook = Livebook.Notebook.Explore.Utils.notebook_from_file!(path)
Module.put_attribute(__MODULE__, attr, notebook)

@external_resource path
end
end

def notebook_from_file!(path) do
markdown = File.read!(path)
{notebook, []} = Livebook.LiveMarkdown.Import.notebook_from_markdown(markdown)
notebook
end
end

defmodule Livebook.Notebook.Explore do
@moduledoc false

import Livebook.Notebook.Explore.Utils

defnotebook(:intro_to_livebook)
defnotebook(:intro_to_elixir)
defnotebook(:intro_to_nx)
defnotebook(:intro_to_axon)
defnotebook(:intro_to_vega_lite)

@type notebook_info :: %{
slug: String.t(),
notebook: Livebook.Notebook.t(),
description: String.t(),
image_url: String.t()
}

@doc """
Returns a list of example notebooks with metadata.
"""
@spec notebook_infos() :: list(notebook_info())
def notebook_infos() do
[
%{
slug: "intro-to-livebook",
notebook: @intro_to_livebook,
description: "Get to know Livebook, see how it works and explore its features.",
image_url: "/images/logo.png"
},
%{
slug: "intro-to-elixir",
notebook: @intro_to_elixir,
description: "New to Elixir? Learn about the language and its core concepts.",
image_url: "/images/elixir.png"
},
%{
slug: "intro-to-nx",
notebook: @intro_to_nx,
description:
"Enter numerical Elixir, experience the power of multi-dimensional arrays of numbers.",
image_url: "/images/nx.png"
},
%{
slug: "intro-to-axon",
notebook: @intro_to_axon,
description: "Build Neural Networks in Elixir using a high-level, composable API.",
image_url: "/images/axon.png"
},
%{
slug: "intro-to-vega-lite",
notebook: @intro_to_vega_lite,
description: "Learn how to quickly create numerous plots for your data.",
image_url: "/images/vega_lite.png"
}
]
end
end
3 changes: 3 additions & 0 deletions lib/livebook/notebook/explore/intro_to_axon.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Neural Networks with Axon

TODO: content 🐈
3 changes: 3 additions & 0 deletions lib/livebook/notebook/explore/intro_to_elixir.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction to Elixir

TODO: content 🐈
213 changes: 213 additions & 0 deletions lib/livebook/notebook/explore/intro_to_livebook.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# Welcome to Livebook

## Introduction

We are happy you decided to give Livebook a try, hopefully it empowers
you to build great stuff! 🚀

Livebook is a tool for crafting **interactive** and **collaborative** code notebooks.
It is primarily meant as a tool for rapid prototyping - think of it as an IEx session
combined with your editor.
You can also use it for authoring shareable articles that people can easily
run and play around with.
Package authors can write notebooks as interactive tutorials
and include them in their repository, so that users can easily download
and run them locally.

## Basic usage

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
and **Elixir** cells where the magic takes place!

To insert a new cell move your cursor between cells and click one of the revealed buttons. 👇

```elixir
# This is an Elixir cell - as the name suggests that's where the code goes.
# To evaluate this cell, you can either press the "Evaluate" button above
# or use `Ctrl + Enter` (or Cmd + Enter on a Mac)!

message = "hey, grab yourself a cup of 🍵"
```

Subsequent cells have access to the bindings you've defined:

```elixir
String.replace(message, "🍵", "☕")
```

Note however that bindings are not global, so each cell *sees* only stuff that goes
above itself. This approach helps to keep the notebook clean and predictable
as you keep working on it!

## Sections

You can leverage so called **sections** to nicely group related cells together.
Click on the "Book" icon in the sidebar to reveal a list of all sections.
As you can see, this approach helps to easily jump around the notebook,
especially once it grows.

Let's make use of this section to see how output is captured!

```elixir
cats = ~w(😼 😹 😻 😺 😸 😽)

for _ <- 1..3 do
cats
|> Enum.take_random(3)
|> Enum.join(" ")
|> IO.puts()
end
```

## Notebook files

By default notebooks are kept in memory, which is fine for interactive hacking,
but oftentimes you will want to save your work for later. Fortunately, notebooks
can be persisted by clicking on the "Disk" icon in the bottom-right corner
and selecting the file location.

Notebooks are stored in **live markdown** format, which is essentially the markdown you know,
with just a few assumptions on how particular elements are represented. Thanks to this
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.

## 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

Livebook uses $\\TeX$ syntax for math.
It supports both inline math like $e^{\\pi i} + 1 = 0$, as well as display math:

$$
S(x) = \\frac{1}{1 + e^{-x}} = \\frac{e^{x}}{e^{x} + 1}
$$

You can explore all supported expressions [here](https://katex.org/docs/supported.html).

## Stepping up your workflow

Once you start using notebooks more, it's gonna be beneficial
to optimise how you move around. Livebook leverages the concept of
**navigation**/**insert** modes and offers many shortcuts for common operations.
Make sure to check out the shortcuts by clicking the "Keyboard" icon in
the sidebar or by typing `?`.

## Final notes

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.

Now go ahead and build something cool! 🚢
3 changes: 3 additions & 0 deletions lib/livebook/notebook/explore/intro_to_nx.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction to Nx

TODO: content 🐈
3 changes: 3 additions & 0 deletions lib/livebook/notebook/explore/intro_to_vega_lite.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Plotting with VegaLite and Kino

TODO: content 🐈