Created
October 17, 2018 13:10
-
-
Save Dzol/2369573728c5802f86dd586c8f60275b to your computer and use it in GitHub Desktop.
16TILUTB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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