Skip to content

Instantly share code, notes, and snippets.

@Dzol
Created October 17, 2018 13:10
Show Gist options
  • Save Dzol/2369573728c5802f86dd586c8f60275b to your computer and use it in GitHub Desktop.
Save Dzol/2369573728c5802f86dd586c8f60275b to your computer and use it in GitHub Desktop.
16TILUTB
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, JupyterTest, <<70, 79, 82, 49, 0, 0, 6, 220, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 13, 0, 0, 0, 30, 18, 69, 108, 105, 120, 105, 114, 46, 74, 117, 112, 121, 116, 101, 114, 84, 101, 115, 116, 8, 95, 95, 105, ...>>, {:context, 0}}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule JupyterTest do\n",
" def go module do\n",
" module.__ex_unit__()\n",
" |> Map.fetch!(:tests)\n",
" |> Enum.each(&run/1)\n",
" end\n",
"\n",
" defp run %ExUnit.Test{module: m, name: f} do\n",
" IO.inspect(f); a = [context()]; Kernel.apply(m, f, a)\n",
" end\n",
"\n",
" defp context do\n",
" %{}\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ExUnit.start(autorun: false)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 16 Things I Learnt Using The BEAM\n",
"\n",
"- 3½ years in 16 sections\n",
"- a lot from the BEAM\n",
"- as much from other communities\n",
"- some immediately usefull, others high-level"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The Umbrella Only Enforces The Bounderies We Create\n",
"\n",
"- it doesn't get bigger picture than the umbrella when it comes to Elixir\n",
"- like the section title says... just like a module\n",
"- absolutely like a collection of repositories (but easier to work w/ at least to begin w/)\n",
"\n",
"```shell\n",
"$ tree -L 1 umbrella/\n",
"umbrella\n",
"├── client\n",
"├── model\n",
"├── server\n",
"└── test\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## OTP & Source Tree\n",
"\n",
"- this is Erlang/OTP's biggest contribution for me\n",
"- Erlang - practical concurrency w/ process + message\n",
"- OTP is declaritive\n",
"- \"concurrency can be hard\" - perhaps but should make code easier in Erlang/Elixir\n",
"\n",
"```shell\n",
"$ tree -L 1 lib/\n",
"lib\n",
"├── x.ex\n",
"└── x\n",
" ├── application.ex\n",
" ├── supervisor.ex\n",
" ├── y.ex\n",
" └── y\n",
" │ └── worker.ex\n",
" ├── z.ex\n",
" └── z\n",
" └── worker.ex\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Domain & Source Tree\n",
"\n",
"- a little more applicable to Elixir\n",
"- Erlang source can reside in a tree too\n",
"\n",
"```shell\n",
"$ tree lib/pox\n",
"lib/pox\n",
"├── http\n",
"│   ├── v1_one\n",
"│   │   ├── request.ex\n",
"│   │   ├── response.ex\n",
"│   │   ├── wire_format\n",
"│   │   │   ├── body.ex\n",
"│   │   │   ├── header.ex\n",
"│   │   │   └── status_line.ex\n",
"│   │   └── wire_format.ex\n",
"│   └── v1_one.ex\n",
"└── http.ex\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naming: Module, Function, & Variable\n",
"\n",
"- it's about building context (or restricting it)\n",
"- who is it that said naming things is hard?\n",
"- module: convey the most information\n",
"- **the heirarchical name space has got to be Elixir's biggest virtue!**\n",
"- variable: module & function name should suffice to such an extent that we can use `x` and `y`\n",
"- we often will not need to spell out a whole name-space, in its entirety, at call site (contradiction below needs refactor)\n",
"\n",
"```elixir\n",
"defmodule Pox.HTTP.V1One.WireFormat.StatusLine do\n",
" def write(x) do\n",
" [x.method, \" \", x.path, \" \", \"HTTP/1.1\"]\n",
" end\n",
"\n",
" def read(x) do\n",
" [protocol, number, _text] = String.split(x, \" \", parts: 3)\n",
" \"HTTP/1.1\" = protocol\n",
" number = String.to_integer(number)\n",
" Pox.HTTP.V1One.Response.Status.known!(number)\n",
" number\n",
" end\n",
"end\n",
"\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Common prefixes, infixes, & suffixes\n",
"\n",
"- if we're repeating an identifier then perhaps we should refactor\n",
"\n",
"```elixir\n",
"Pox.HTTP.Request.http_post(request_data)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Isolation: the function is chief\n",
"\n",
"- we often say, in the BEAM community, that processes are about isolation\n",
"- they are but our first line of isolation should be the function\n",
"- No side-effect\n",
"- No side-cause\n",
"- Paul Graham explains this perfectly in the opening of his book \"On Lisp\"\n",
"- this is what we call _intentional programming_ in the Erlang world and _assertive programming_ in the Elixir world\n",
"- success and failure are all about scope\n",
"- a functions context, and its locals, should be enough to explain failure... so\n",
"- processes are about fault tolerance\n",
"\n",
"```elixir\n",
"def read(x) do\n",
" [protocol, number, _text] = String.split(x, \" \", parts: 3)\n",
" \"HTTP/1.1\" = protocol\n",
" number = String.to_integer(number)\n",
" Pox.HTTP.V1One.Response.Status.known!(number)\n",
" number\n",
"end\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constructor name: `default/0` & `instance/1`\n",
"\n",
"- FP may be all about our data (ATDs)\n",
"- occasionally, for complex data, a litteral doesn't suffice\n",
"- we have to check arguments & astract them away\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, Psi.LinearCongruentialGenerator, <<70, 79, 82, 49, 0, 0, 13, 172, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 174, 0, 0, 0, 41, 38, 69, 108, 105, 120, 105, 114, 46, 80, 115, 105, 46, 76, 105, 110, 101, 97, 114, 67, 111, 110, 103, 114, ...>>, {:default, 0}}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule Psi.LinearCongruentialGenerator do\n",
" @moduledoc \"\"\"\n",
" A Linear Congruential Generator\n",
" ## Example\n",
" iex> alias Psi.LinearCongruentialGenerator, as: LCG\n",
" Psi.LinearCongruentialGenerator\n",
" iex> LCG.stream(seed: 7) |> Psi.PairStream.take(3)\n",
" [7, 6, 9]\n",
" \"\"\"\n",
"\n",
" @typedoc \"\"\"\n",
" Main interface for building an LCG\n",
" See _Wikipedia_ or _Knuth_ to select values for these parameters.\n",
" The current default configuration is awful.\n",
" \"\"\"\n",
" @type t :: %__MODULE__{\n",
" modulus: integer(),\n",
" multiplier: integer(),\n",
" increment: integer()\n",
" }\n",
" @enforce_keys [\n",
" :modulus,\n",
" :multiplier,\n",
" :increment\n",
" ]\n",
" defstruct [\n",
" modulus: 0,\n",
" multiplier: 0,\n",
" increment: 0\n",
" ]\n",
"\n",
" @doc \"\"\"\n",
" An Linear Congruential Generator with the given parameters\n",
" \"\"\"\n",
" @spec instance(t) :: (integer -> integer)\n",
" def instance(%__MODULE__{modulus: m, multiplier: a, increment: c}) when (m > 0) and (m > a and a >= 0) and (m > c and c >= 0) do\n",
" & Integer.mod((a * &1) + c, m)\n",
" end\n",
"\n",
" @spec default() :: t\n",
" def default do\n",
" instance(struct!(__MODULE__, Application.fetch_env!(:psi, :default)))\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
":ok"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Application.put_env(:psi, :default, [ modulus: 10, multiplier: 7, increment: 7 ])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## default/1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Application Site & Definition Site\n",
"\n",
"- identifiers at application site may tell us little about how they're intended to be use w/ some other functionality"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, Shape, <<70, 79, 82, 49, 0, 0, 4, 24, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 133, 0, 0, 0, 14, 12, 69, 108, 105, 120, 105, 114, 46, 83, 104, 97, 112, 101, 8, 95, 95, 105, 110, 102, 111, 95, 95, 9, ...>>, {:rectangle, 2}}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule Shape do\n",
" def rectangle(width, height) do\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Shape.rectangle(3, 2)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"width = 3; height = 2\n",
"Shape.rectangle(width, height)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, Shape, <<70, 79, 82, 49, 0, 0, 4, 68, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 137, 0, 0, 0, 16, 12, 69, 108, 105, 120, 105, 114, 46, 83, 104, 97, 112, 101, 8, 95, 95, 105, 110, 102, 111, 95, 95, 9, ...>>, {:rectangle, 1}}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule Shape do\n",
" def rectangle(x: width, y: height) do\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Shape.rectangle(x: 3, y: 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- the `Access` protocol so that order does not matter\n",
"- `Shape.rectangle` builds a data structure (presumably) so bad example\n",
"- `Interval.between?(3, start: 2, stop: 5)`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Coding Bottom-Up & Coding Top-Down\n",
"\n",
"- historical Bottom-Up write/read\n",
"- abstraction: from general to specific\n",
"- we want Top-Down\n",
"- variable are bottom-up\n",
" - They force the reader to read depth-first\n",
" - They force the writer to write depth-first\n",
"- functions are top-down\n",
" - They are lazy in this regard w.r.t to writing/reading code"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, M, <<70, 79, 82, 49, 0, 0, 5, 56, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 156, 0, 0, 0, 20, 8, 69, 108, 105, 120, 105, 114, 46, 77, 8, 95, 95, 105, 110, 102, 111, 95, 95, 9, 102, 117, 110, 99, ...>>, {:h, 1}}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule M do\n",
" def f do\n",
" x = 16 * g(64) + M.h(32)\n",
" g(b(x))\n",
" end\n",
"\n",
" defp g(_a) do\n",
" end\n",
"\n",
" defp h(_b) do\n",
" end\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pattern Matching\n",
"\n",
"- litteral data - data centric paragdim\n",
"- FP is symbolic - intuitive\n",
"- too big on occasion\n",
"- we want ADTs and APIs\n",
"- push PM to the leaves of a project"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"%{baz: %{\"bongo\" => %{bash: \"bingo\"}}, fu: \"bar\"}"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = %{\n",
" fu: \"bar\",\n",
" baz: %{\n",
" \"bongo\" => %{\n",
" bash: \"bingo\"\n",
" }\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"##f = fn (...) when ... ->\n",
"## x\n",
"##end"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## f.(x)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## Kernel.get_in/2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Closure\n",
"\n",
"- FP's pearl\n",
"- like pattern matching when closures get too big\n",
"- rely on other aspects of 1st class function values"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"#Function<6.99386804/1 in :erl_eval.expr/5>"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n = 3; f = fn (x) ->\n",
" n * x\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f.(2)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## Mu.factor/1"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## f = ..."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## f.(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `=` much more like `let` than assignment\n",
"\n",
"- FP's are not assignment heavy languages\n",
"- they don't relay on mutation (statement by statement)\n",
"- they are value centric\n",
"- we rely on expression after expresion\n",
"- repetition takes the form of recursion or HOF\n",
"- binding a variable is good for two things:\n",
" - pattern match elements\n",
" - assign a value we got from something side-effecty/side-causy"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[\"README.md\", \"test\", \"install_script.sh\", \"priv\", \"deps\", \".git\", \".travis.yml\", \"mix.exs\", \"prod_db.sqlite3\", \"LICENSE\", \"mix.lock\", \"docker\", \"test_db.sqlite3\", \"lib\", \"resources\", \"config\", \"_build\", \"start_script.sh\", \"envs\", \".gitignore\"]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = File.ls!()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The Bit Syntax\n",
"\n",
"- pattern matching for binary data\n",
"- perhaps Erlang's most loved notation\n",
"- unique to Erlang (and Elixir) AFAIK"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, X.DNS.Query, <<70, 79, 82, 49, 0, 0, 4, 236, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 168, 0, 0, 0, 16, 18, 69, 108, 105, 120, 105, 114, 46, 88, 46, 68, 78, 83, 46, 81, 117, 101, 114, 121, 8, 95, 95, 105, ...>>, {:encode, 0}}"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule X.DNS.Query do\n",
" defmodule URL do\n",
" def encode do\n",
" <<\n",
" 0x07, 0x65, # 'example' has length 7, e\n",
" 0x78, 0x61, # x, a\n",
" 0x6D, 0x70, # m, p\n",
" 0x6C, 0x65, # l, e\n",
" 0x03, 0x63, # 'com' has length 3, c\n",
" 0x6F, 0x6D, # o, m\n",
" >>\n",
" end\n",
" end\n",
"\n",
" def encode do\n",
" URL.encode()\n",
" <>\n",
" <<0x00>>\n",
" <>\n",
" <<\n",
" # QTYPE \n",
" 0x00,\n",
" 0x01,\n",
" # QCLASS\n",
" 0x00,\n",
" 0x01\n",
" >>\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<<7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 1, 0, 1>>"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X.DNS.Query.encode()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Processes: capture the operational\n",
"\n",
"- _domain_ captured w/ a functional core\n",
" - models\n",
" - relationships\n",
" - transformation\n",
"- _delivery_ captured w/ an operational shell\n",
" - DB\n",
" - Queue\n",
" - JSON/HTTP API"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The Ultimate Functional Evil: Dependency Injection\n",
"\n",
"- if we're relying on DI to test then we can revise our model"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test w/ `assert` as close as possible to success/failure condition\n",
"\n",
"- this will give us maximal information if/when a test case fails"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{:module, XTest, <<70, 79, 82, 49, 0, 0, 11, 84, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 87, 0, 0, 0, 35, 12, 69, 108, 105, 120, 105, 114, 46, 88, 84, 101, 115, 116, 8, 95, 95, 105, 110, 102, 111, 95, 95, 9, ...>>, {:\"test The bad way\", 1}}"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"defmodule XTest do\n",
" use ExUnit.Case, async: true\n",
"\n",
" test \"The bad way\" do\n",
" import Integer, only: [is_even: 1]\n",
" x = [0, 2, 4, 5, 6]\n",
" assert Enum.all?(x, &is_even/1)\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"ename": "ExUnit.AssertionError",
"evalue": "1",
"output_type": "error",
"traceback": [
"** %ExUnit.AssertionError{expr: {:assert, [], [{{:., [line: 7], [{:__aliases__, [line: 7], [:Enum]}, :all?]}, [line: 7], [{:x, [line: 7], nil}, {:&, [line: 7], [{:/, [line: 7], [{:is_even, [line: 7], nil}, 1]}]}]}]}, left: :ex_unit_no_meaningful_value, message: \"Expected truthy, got false\", right: :ex_unit_no_meaningful_value}"
]
}
],
"source": [
"JupyterTest.go XTest"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## Enum.each/2"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nil"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#JupyterTest.go XTest"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Thank You!\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Elixir",
"language": "Elixir",
"name": "ielixir"
},
"language_info": {
"codemirror_mode": "elixir",
"file_extension": "ex",
"mimetype": "text/x-elixir",
"name": "elixir",
"nbconvert_exporter": "",
"pygments_lexer": "elixir",
"version": "#Version<1.6.6>"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment