First commit

This commit is contained in:
Mariano Uvalle 2020-06-20 13:57:33 -05:00
commit 1dba0af064
36 changed files with 1065 additions and 0 deletions

BIN
Elixir.Circle.beam Normal file

Binary file not shown.

1
README.md Normal file
View file

@ -0,0 +1 @@
# elixir-in-action

21
aliases.ex Normal file
View file

@ -0,0 +1,21 @@
defmodule Some.Nested do
def print_from_nested do
IO.puts("Somehting from nested")
end
end
defmodule MyModule do
import IO
alias IO, as: MyIO
alias Some.Nested
def print do
puts("Something")
end
def print_from_alias do
MyIO.puts("Something from alias")
end
def print_from_nested, do: Nested.print_from_nested()
end

21
arity_calc.ex Normal file
View file

@ -0,0 +1,21 @@
defmodule CalcOne do
def sum(a) do
sum(a, 0)
end
def sum(a, b) do
a + b
end
end
defmodule CalcTwo do
def sum(a, b \\ 0) do
a + b
end
end
defmodule CalcThree do
def fun(a, b, c \\ 0, d, e \\ 0) do
a + b + c + d + e
end
end

View file

@ -0,0 +1,37 @@
defmodule FileHelper do
defp filtered_lines!(path) do
File.stream!(path)
|> Stream.map(&String.replace(&1, "\n", ""))
end
def line_lengths!(path) do
path
|> filtered_lines!()
|> Enum.map(&String.length/1)
end
def longest_line_length(path) do
path
|> filtered_lines!()
|> Stream.map(&String.length/1)
|> Enum.max()
end
def longest_line(path) do
path
|> filtered_lines!()
|> Enum.max_by(&String.length/1)
end
def words_per_line(path) do
path
|> filtered_lines!()
|> Enum.map(&word_count/1)
end
def word_count(string) do
string
|> String.split()
|> length()
end
end

View file

@ -0,0 +1,6 @@
This is a file
that contains some words
and is mean to be just an example
to be used to practice
with elixir streams
and enums

8
chapter3/natural_nums.ex Normal file
View file

@ -0,0 +1,8 @@
defmodule NaturalNums do
def print(1), do: IO.puts(1)
def print(n) when is_integer(n) and n > 1 do
print(n - 1)
IO.puts(n)
end
end

View file

@ -0,0 +1,21 @@
defmodule ListHelper do
def list_len([]), do: 0
def list_len([_ | tail]) do
1 + list_len(tail)
end
def range(num, num), do: [num]
def range(num1, num2) do
[num1 | range(num1 + 1, num2)]
end
def positive([]), do: []
def positive([head | tail]) when head > 0 do
[head | positive(tail)]
end
def positive([_ | tail]), do: positive(tail)
end

View file

@ -0,0 +1,33 @@
defmodule ListHelper do
def list_len(list) do
list_len_helper(0, list)
end
defp list_len_helper(current_len, []), do: current_len
defp list_len_helper(current_len, [_ | tail]) do
list_len_helper(current_len + 1, tail)
end
def range(num1, num2), do: range_helper([], num1, num2)
defp range_helper(current_list, num, num), do: [num | current_list]
defp range_helper(current_list, num1, num2) do
range_helper([num2 | current_list], num1, num2 - 1)
end
def positive(list) do
Enum.reverse(positive_helper([], list))
end
defp positive_helper(current_list, []), do: current_list
defp positive_helper(current_list, [head | tail]) when head > 0 do
positive_helper([head | current_list], tail)
end
defp positive_helper(current_list, [_ | tail]) do
positive_helper(current_list, tail)
end
end

7
chapter3/sum_list.ex Normal file
View file

@ -0,0 +1,7 @@
defmodule ListHelper do
def sum([]), do: 0
def sum([head | tail]) do
head + sum(tail)
end
end

16
chapter3/sum_list_tc.ex Normal file
View file

@ -0,0 +1,16 @@
defmodule ListHelper do
def sum(list) do
do_sum(0, list)
end
defp do_sum(current_sum, []) do
current_sum
end
defp do_sum(current_sum, [head | tail]) do
# More concise implementation
# do_sum(current_sum + head, tail)
new_sum = head + current_sum
do_sum(new_sum, tail)
end
end

View file

@ -0,0 +1,40 @@
defmodule UserData do
defp extract_login(%{"login" => login}), do: {:ok, login}
defp extract_login(_), do: {:error, "login missing"}
defp extract_password(%{"password" => password}), do: {:ok, password}
defp extract_password(_), do: {:error, "password mising"}
defp extract_email(%{"email" => email}), do: {:ok, email}
defp extract_email(_), do: {:error, "email missing"}
def extract_user_case(user) do
case extract_login(user) do
{:error, reason} ->
{:error, reason}
{:ok, login} ->
case extract_email(user) do
{:error, reason} ->
{:error, reason}
{:ok, email} ->
case extract_password(user) do
{:error, reason} ->
{:error, reason}
{:ok, password} ->
%{login: login, email: email, password: password}
end
end
end
end
def extract_user_with(user) do
with {:ok, login} <- extract_login(user),
{:ok, email} <- extract_email(user),
{:ok, password} <- extract_password(user) do
{:ok, %{login: login, email: email, password: password}}
end
end
end

View file

@ -0,0 +1,11 @@
defmodule UserExtraction do
def extract(user) do
case Enum.filter(["login", "email", "password"], &(not Map.has_key?(user, &1))) do
[] ->
{:ok, %{login: user["login"], email: user["email"], password: user["password"]}}
missing_fields ->
{:error, "missing fields: #{Enum.join(missing_fields, ", ")}"}
end
end
end

3
chapter4/files/todos.csv Normal file
View file

@ -0,0 +1,3 @@
2018/12/19,Dentist
2018/12/20,Shopping
2018/12/19,Movies
1 2018/12/19 Dentist
2 2018/12/20 Shopping
3 2018/12/19 Movies

16
chapter4/fraction.ex Normal file
View file

@ -0,0 +1,16 @@
defmodule Fraction do
defstruct a: nil, b: nil
def new(a, b), do: %Fraction{a: a, b: b}
def value(%Fraction{a: a, b: b}) do
a / b
end
def add(%Fraction{a: a1, b: b1}, %Fraction{a: a2, b: b2}) do
new(
a1 * b2 + a2 * b1,
b1 * b2
)
end
end

16
chapter4/simple_todo.ex Normal file
View file

@ -0,0 +1,16 @@
defmodule TodoList do
def new(), do: %{}
def add_entry(todo_list, date, title) do
Map.update(
todo_list,
date,
[title],
&[title | &1]
)
end
def entries(todo_list, date) do
Map.get(todo_list, date, [])
end
end

59
chapter4/todo_builder.ex Normal file
View file

@ -0,0 +1,59 @@
defmodule TodoList do
defstruct auto_id: 1, entries: %{}
def new(), do: %TodoList{}
def new(entries \\ []) do
Enum.reduce(
entries,
%TodoList{},
fn entry, todo_list_acc ->
add_entry(todo_list_acc, entry)
end
# Alternative definition
# &add_entry(&2, &1)
)
end
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.auto_id)
new_entries =
Map.put(
todo_list.entries,
todo_list.auto_id,
entry
)
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
end
def entries(todo_list, date) do
todo_list.entries
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|> Enum.map(fn {_, entry} -> entry end)
end
def update_entry(todo_list, %{} = new_entry) do
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
old_entry_id = old_entry.id
# Make sure that the result of the updater is a map and the
# id remains unchanged.
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%TodoList{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end

47
chapter4/todo_crud.ex Normal file
View file

@ -0,0 +1,47 @@
defmodule TodoList do
defstruct auto_id: 1, entries: %{}
def new(), do: %TodoList{}
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.auto_id)
new_entries =
Map.put(
todo_list.entries,
todo_list.auto_id,
entry
)
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
end
def entries(todo_list, date) do
todo_list.entries
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|> Enum.map(fn {_, entry} -> entry end)
end
def update_entry(todo_list, %{} = new_entry) do
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
old_entry_id = old_entry.id
# Make sure that the result of the updater is a map and the
# id remains unchanged.
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%TodoList{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end

126
chapter4/todo_import.ex Normal file
View file

@ -0,0 +1,126 @@
defmodule TodoList do
defstruct auto_id: 1, entries: %{}
def new(entries \\ []) do
Enum.reduce(
entries,
%TodoList{},
fn entry, todo_list_acc ->
add_entry(todo_list_acc, entry)
end
# Alternative definition
# &add_entry(&2, &1)
)
end
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.auto_id)
new_entries =
Map.put(
todo_list.entries,
todo_list.auto_id,
entry
)
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
end
def entries(todo_list, date) do
todo_list.entries
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|> Enum.map(fn {_, entry} -> entry end)
end
def update_entry(todo_list, %{} = new_entry) do
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
old_entry_id = old_entry.id
# Make sure that the result of the updater is a map and the
# id remains unchanged.
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%TodoList{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end
defmodule TodoList.CsvImporter do
def import(path) do
File.stream!(path)
|> Stream.map(&String.replace(&1, "\n", ""))
|> Stream.map(fn string ->
[date, title] = String.split(string, ",")
{date, title}
end)
|> Stream.map(fn {date, title} ->
[year, month, day] =
date
|> String.split("/")
|> Enum.map(&String.to_integer(&1))
{{year, month, day}, title}
end)
|> Stream.map(fn {{year, month, day}, title} ->
{:ok, date} = Date.new(year, month, day)
%{date: date, title: title}
end)
|> TodoList.new()
end
end
defmodule TodoList.CsvImporterAlternative do
def import(path) do
path
|> read_lines
|> create_entires
|> TodoList.new()
end
defp read_lines(path) do
path
|> File.stream!()
|> Stream.map(&String.replace(&1, "\n", ""))
end
defp create_entires(lines) do
lines
|> Stream.map(&extract_fileds/1)
|> Stream.map(&create_entry/1)
end
defp extract_fileds(line) do
line
|> String.split(",")
|> convert_date
end
defp convert_date([date_string, title]) do
{parse_date(date_string), title}
end
defp parse_date(date_string) do
[year, month, day] =
date_string
|> String.split("/")
|> Enum.map(&String.to_integer/1)
{:ok, date = Date.new(year, month, day)}
date
end
defp create_entry({date, title}) do
%{date: date, title: title}
end
end

View file

@ -0,0 +1,28 @@
defmodule MultiDict do
def new(), do: %{}
def add(dict, key, value) do
Map.update(
dict,
key,
[value],
&[value | &1]
)
end
def get(dict, key) do
Map.get(dict, key, [])
end
end
defmodule TodoList do
def new(), do: MultiDict.new()
def add_entry(todo_list, entry) do
MultiDict.add(todo_list, entry.date, entry)
end
def entries(todo_list, date) do
MultiDict.get(todo_list, date)
end
end

View file

@ -0,0 +1,28 @@
defmodule MultiDict do
def new(), do: %{}
def add(dict, key, value) do
Map.update(
dict,
key,
[value],
&[value | &1]
)
end
def get(dict, key) do
Map.get(dict, key, [])
end
end
defmodule TodoList do
def new(), do: MultiDict.new()
def add_entry(todo_list, date, title) do
MultiDict.add(todo_list, date, title)
end
def entries(todo_list, date) do
MultiDict.get(todo_list, date)
end
end

46
chapter5/calculator.ex Normal file
View file

@ -0,0 +1,46 @@
defmodule Calculator do
def start do
spawn(fn -> loop(0) end)
end
defp loop(current_value) do
new_value =
receive do
{:value, caller} ->
send(caller, {:response, current_value})
current_value
{:add, value} ->
current_value + value
{:sub, value} ->
current_value - value
{:mul, value} ->
current_value * mul
{:div, value} ->
current_value / value
invalid_request ->
IO.puts("invalid request #{inspect(invalid_request)}")
current_value
end
loop(new_value)
end
def value(server_pid) do
send(server_pid, {:value, self()})
receive do
{:response, value} ->
value
end
end
def add(server_pid, value), do: send(server_pid, {:add, value})
def sub(server_pid, value), do: send(server_pid, {:sub, value})
def mul(server_pid, value), do: send(server_pid, {:mul, value})
def div(server_pid, value), do: send(server_pid, {:div, value})
end

View file

@ -0,0 +1,35 @@
defmodule Calculator do
def start do
spawn(fn -> loop(0) end)
end
defp loop(current_value) do
new_value =
receive do
message -> process_message(current_value, message)
end
loop(new_value)
end
defp process_message(current_value, {:value, caller}) do
send(caller, {:response, current_value})
current_value
end
defp process_message(current_value, {:add, value}) do
current_value + value
end
defp process_message(current_value, {:sub, value}) do
current_value - value
end
defp process_message(current_value, {:mul, value}) do
current_value * value
end
defp process_message(current_value, {:div, value}) do
current_value / value
end
end

View file

@ -0,0 +1,31 @@
defmodule DatabaseServer do
def start do
spawn(&loop/0)
end
defp loop do
receive do
{:run_query, caller, query_def} ->
send(caller, {:query_result, run_query(query_def)})
end
loop()
end
defp run_query(query_def) do
Process.sleep(2000)
"#{query_def} result"
end
def run_async(server_pid, query_def) do
send(server_pid, {:run_query, self(), query_def})
end
def get_result do
receive do
{:query_result, result} -> result
after
5000 -> {:error, :timeout}
end
end
end

View file

@ -0,0 +1,23 @@
defmodule Server do
def start do
spawn(fn -> loop() end)
end
def send_msg(server, message) do
send(server, {self(), message})
receive do
{:response, response} -> response
end
end
def loop do
receive do
{caller, message} ->
Process.sleep(1000)
send(caller, {:response, message})
end
loop()
end
end

View file

@ -0,0 +1,110 @@
defmodule TodoServer do
def start do
process_pid = spawn(fn -> loop(TodoList.new()) end)
Process.register(process_pid, :todo_server)
end
# Alternative for the start function
# def start do
# spawn(fn ->
# Process.register(self(), :todo_server)
# loop(TodoList.new())
# end)
# end
def add_entry(new_entry) do
send(:todo_server, {:add_entry, new_entry})
end
def entries(date) do
send(:todo_server, {:entries, self(), date})
receive do
{:todo_etries, entries} -> entries
after
5000 -> {:error, :timeout}
end
end
def update_entry(entry_id, updater_fun) do
send(:todo_server, {:update_entry, entry_id, updater_fun})
end
def delete_entry(entry_id) do
send(:todo_server, {:delete_entry, entry_id})
end
defp loop(todo_list) do
new_todo_list =
receive do
message -> process_message(todo_list, message)
end
loop(new_todo_list)
end
defp process_message(todo_list, {:add_entry, new_entry}) do
TodoList.add_entry(todo_list, new_entry)
end
defp process_message(todo_list, {:entries, caller, date}) do
send(caller, {:todo_etries, TodoList.entries(todo_list, date)})
todo_list
end
defp process_message(todo_list, {:update_entry, entry_id, updater_fun}) do
TodoList.update_entry(todo_list, entry_id, updater_fun)
end
defp process_message(todo_list, {:delete_entry, entry_id}) do
TodoList.delete_entry(todo_list, entry_id)
end
end
defmodule TodoList do
defstruct auto_id: 1, entries: %{}
def new(), do: %TodoList{}
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.auto_id)
new_entries =
Map.put(
todo_list.entries,
todo_list.auto_id,
entry
)
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
end
def entries(todo_list, date) do
todo_list.entries
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|> Enum.map(fn {_, entry} -> entry end)
end
def update_entry(todo_list, %{} = new_entry) do
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
old_entry_id = old_entry.id
# Make sure that the result of the updater is a map and the
# id remains unchanged.
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%TodoList{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end

View file

@ -0,0 +1,34 @@
defmodule DatabaseServer do
def start do
spawn(fn ->
connection = :rand.uniform(1000)
loop(connection)
end)
end
defp loop(connection) do
receive do
{:run_query, caller, query_def} ->
send(caller, {:query_result, run_query(connection, query_def)})
end
loop(connection)
end
defp run_query(connection, query_def) do
Process.sleep(2000)
"Connection #{connection}: #{query_def} result"
end
def run_async(server_pid, query_def) do
send(server_pid, {:run_query, self(), query_def})
end
def get_result do
receive do
{:query_result, result} -> result
after
5000 -> {:error, :timeout}
end
end
end

101
chapter5/todo_server.ex Normal file
View file

@ -0,0 +1,101 @@
defmodule TodoServer do
def start do
spawn(fn -> loop(TodoList.new()) end)
end
def add_entry(todo_server, new_entry) do
send(todo_server, {:add_entry, new_entry})
end
def entries(todo_server, date) do
send(todo_server, {:entries, self(), date})
receive do
{:todo_etries, entries} -> entries
after
5000 -> {:error, :timeout}
end
end
def update_entry(todo_server, entry_id, updater_fun) do
send(todo_server, {:update_entry, entry_id, updater_fun})
end
def delete_entry(todo_server, entry_id) do
send(todo_server, {:delete_entry, entry_id})
end
defp loop(todo_list) do
new_todo_list =
receive do
message -> process_message(todo_list, message)
end
loop(new_todo_list)
end
defp process_message(todo_list, {:add_entry, new_entry}) do
TodoList.add_entry(todo_list, new_entry)
end
defp process_message(todo_list, {:entries, caller, date}) do
send(caller, {:todo_etries, TodoList.entries(todo_list, date)})
todo_list
end
defp process_message(todo_list, {:update_entry, entry_id, updater_fun}) do
TodoList.update_entry(todo_list, entry_id, updater_fun)
end
defp process_message(todo_list, {:delete_entry, entry_id}) do
TodoList.delete_entry(todo_list, entry_id)
end
end
defmodule TodoList do
defstruct auto_id: 1, entries: %{}
def new(), do: %TodoList{}
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.auto_id)
new_entries =
Map.put(
todo_list.entries,
todo_list.auto_id,
entry
)
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
end
def entries(todo_list, date) do
todo_list.entries
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|> Enum.map(fn {_, entry} -> entry end)
end
def update_entry(todo_list, %{} = new_entry) do
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
old_entry_id = old_entry.id
# Make sure that the result of the updater is a map and the
# id remains unchanged.
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%TodoList{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end

View file

@ -0,0 +1,55 @@
defmodule ServerProcess do
def start(callback_module) do
spawn(fn ->
initial_state = callback_module.init()
loop(callback_module, initial_state)
end)
end
def loop(callback_module, current_state) do
receive do
{request, caller} ->
{response, new_state} =
callback_module.handle_call(
request,
current_state
)
send(caller, {:response, response})
loop(callback_module, new_state)
end
end
def call(server_pid, request) do
send(server_pid, {request, self()})
receive do
{:response, response} ->
response
end
end
end
defmodule KeyValueStore do
def init, do: %{}
def start do
ServerProcess.start(KeyValueStore)
end
def put(pid, key, value) do
ServerProcess.call(pid, {:put, key, value})
end
def get(pid, key) do
ServerProcess.call(pid, {:get, key})
end
def handle_call({:put, key, value}, state) do
{:ok, Map.put(state, key, value)}
end
def handle_call({:get, key}, state) do
{Map.get(state, key), state}
end
end

12
circle.ex Normal file
View file

@ -0,0 +1,12 @@
defmodule Circle do
@moduledoc "Implements basic circle functions"
@pi 3.14159
@doc "Computes the area of a circle"
@spec area(number) :: number
def area(r), do: r * r * @pi
@doc "Computes the circumferenc of a circle"
@spec circumference(number) :: number
def circumference(r), do: 2 * r * @pi
end

30
geometry.ex Normal file
View file

@ -0,0 +1,30 @@
defmodule Geometry do
def rectangle_area(a, b) do
a * b
end
def rectangle_area_condensed(a, b), do: a * b
end
defmodule Rectangle do
def area(a, b), do: a * b
def area(a), do: area(a, a)
end
defmodule GeometryMultiClause do
def area({:rectangle, a, b}) do
a * b
end
def area({:square, a}) do
a * a
end
def area({:circle, r}) do
r * r * 3.14159
end
def area(unknown) do
{:error, {:unkown_shape, unknown}}
end
end

9
private_fun.ex Normal file
View file

@ -0,0 +1,9 @@
defmodule TestPrivate do
def double(a) do
sum(a, a)
end
defp sum(a, b) do
a + b
end
end

5
rect.ex Normal file
View file

@ -0,0 +1,5 @@
defmodule Rectangle do
def area({a, b}) do
a * b
end
end

7
script.exs Normal file
View file

@ -0,0 +1,7 @@
defmodule MyModule do
def run do
IO.puts("Called MyModule run!")
end
end
MyModule.run()

11
test_num.ex Normal file
View file

@ -0,0 +1,11 @@
defmodule TestNum do
def test(x) when x < 0 do
:negative
end
def test(0), do: :zero
def test(x) when x > 0 do
:positive
end
end

11
test_num2.ex Normal file
View file

@ -0,0 +1,11 @@
defmodule TestNum do
def test(x) when is_number(x) and x < 0 do
:negative
end
def test(0), do: :zero
def test(x) when is_number(x) and x > 0 do
:positive
end
end