Run this notebook

Use Livebook to open this notebook and explore new ideas.

It is easy to get started, on your machine or the cloud.

Click below to open and run it in your Livebook at .

(or change your Livebook location)

<!-- vim: set syntax=markdown: --> # Stephen's Strange Leaflet about Elixir - Page 4 ```elixir Mix.install([ {:kino, "~> 0.6.1"} ]) ``` ## If you don't have this open in Livebook you should [![Run in Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fsdball%2Flivebooks%2Fblob%2Fmain%2Fbooks%2Fstrange-leaflet-about-elixir%2Fpage4.livemd) ## Thinking in processes Shifting to thinking in processes is one of the biggest leaps that separates someone who knows the Elixir language syntax from someone who writes idiomatic Elixir. You can absolutely write big giant processes that do a lot of work iteratively and then complain that Elixir isn't a magic wand for concurrency at all and it's slow and annoying and you don't see what all the fuss is about. That would be very sad. But you could do it. Like how lawnmower man was trying password combinations iteratively one by one when he was trying to escape the mainframe. Dude got lucky. Or you could let go your earthly tether, empty, and become wind. ## A process working from top to bottom Let's say we have a password system. If we use `my voice is my passport` then we gain access. If we use anything else then we have to wait three seconds and get an error response. ```elixir defmodule PasswordSystem do def check("my voice is my passport") do {:ok, :access_granted} end def check(_password) do Process.sleep(3000) {:error, :access_denied} end end ``` ```elixir PasswordSystem.check("setec astronomy") ``` ```elixir PasswordSystem.check("reindeer flotilla") ``` ```elixir PasswordSystem.check("my voice is my passport") ``` Let's say we're hacking the system. If we wanted to try a list of passwords against the password system one by one then we'd have to wait three seconds per guess! For only a hundred passwords that'd be almost five minutes of waiting if we were unlucky enough to have the right password at the end of the list. If we had the right password in the list at all. ```elixir cracked_password = 1..3 |> Enum.into([]) |> then(fn list -> list ++ ["my voice is my passport"] ++ [5, 6, 7] end) |> IO.inspect(label: "password guesses") |> Enum.find(fn guess -> {:ok, :access_granted} == PasswordSystem.check(guess) end) if !is_nil(cracked_password) do IO.puts("We're in 😎 the password is: #{inspect(cracked_password)}") end ``` No. We're serious hackers with sunglasses and a powerglove. That kind of waiting won't do at all! We won't limit ourselves to one guess at a time. We'll guess them all at once because this password system doesn't have any rate limits. ## Tasks One of the simplest ways to spawn a new process is the top level `spawn/1` function or `Process.spawn/2`. They spawn a process with the given function and then the function completes the processes die. ```elixir pid = spawn(fn -> 3 + 1 end) Process.alive?(pid) |> IO.inspect(label: "process alive immediately after spawn?") Process.sleep(100) Process.alive?(pid) |> IO.inspect(label: "process alive after 100ms?") ``` You'll likely note that there's no way to get at the function result of that spawned process. We can't dig into the memory or state of that process from the outside. And we can't send it a message to ask for the result because 1) it's dead already and 2) we never taught it how to respond to messages anyway. To get a result back the Elixir approach of thinking in processes is: send a message! ```elixir # note who we are origin = self() # spawn off the work, note the closure allowing the anonymous function to have `origin` spawn(fn -> send(origin, {:response, 4 + 1}) end) # receive the answer, waiting up to 100ms receive do {:response, answer} -> IO.puts("We got an answer! #{answer}") after 100 -> IO.puts("no messages after 100ms") end ``` As you may be starting to suspect, `spawn/1` is a simple function to kick off another process at a pretty low level of abstraction. We have higher levels of abstraction available and unless things are real weird we should use them instead. ## The Task module Elixir provides the `Task` module to be a nice abstraction around sending off units of work for which we may or may not want a result. Let's use `Task` to crack our password! First, here's how to queue a task and get its result. No need for us high level programmers to think about the coordination of sending/receiving messages! ```elixir task = Task.async(fn -> 1 + 3 end) Task.await(task) ``` Let's spawn off an async Task per password guess and get this hack going! ```elixir 1..1000 |> Enum.into([]) |> then(fn list -> list ++ ["my voice is my passport"] end) |> then(fn guesses -> IO.inspect(Enum.count(guesses), label: "password guesses count") guesses end) |> Enum.map(fn guess -> Task.async(fn -> case PasswordSystem.check(guess) do {:ok, :access_granted} -> {:ok, guess} _ -> {:error, guess} end end) end) |> Task.await_many() |> Enum.find(fn result -> case result do {:ok, _password} -> true _ -> false end end) |> then(fn result -> case result do {:ok, password} -> IO.puts("We're in 😎 the password is: #{inspect(password)}") nil -> IO.puts("Our hack failed noooooo!") end end) ``` What did we just do there?! Well 1. We mapped 1000 numbers into a list of guesses 2. Appended the actual password to the end (our worst case scenario for iteration) 3. Mapped each of those to `Task.async/1` 4. Handed that resulting list to `Task.await_many/1` which knows how to wait for a list of tasks 5. Checked through our list of results to find out if any of the guesses was the right password. 6. Print out a success or failure This is the slightest dip into the world of Elixir processes. But it's a start! <!-- livebook:{"break_markdown":true} --> « [back to page 3](page3.livemd) || [turn to page 5](page5.livemd) »
See source

Have you already installed Livebook?

If you already installed Livebook, you can configure the default Livebook location where you want to open notebooks.
Livebook up Checking status We can't reach this Livebook (but we saved your preference anyway)
Run notebook

Not yet? Install Livebook in just a minute

Livebook is open source, free, and ready to run anywhere.

Run on your machine

with Livebook Desktop

Run in the cloud

on select platforms

To run on Linux, Docker, embedded devices, or Elixir’s Mix, check our README.

PLATINUM SPONSORS
SPONSORS
Code navigation with go to definition of modules and functions Read More ×