Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Elixir in Elixir

1433d0176e764f9ce48f70673baa72e8?s=47 Jay Hayes
September 02, 2016

Elixir in Elixir

Metaprogramming doesn't have to be scary. Elixir does it really well. Let me show you...

1433d0176e764f9ce48f70673baa72e8?s=128

Jay Hayes

September 02, 2016
Tweet

Transcript

  1. Elixir Elixir In

  2. How much?

  3. It depends

  4. What is “Elixir”?

  5. Basics + STDLIB

  6. of Elixir is written in Elixir 78%

  7. Compare to Ruby’s 44%

  8. Numbers? Meh

  9. if

  10. def

  11. |>

  12. Metaprogramming

  13. Code generation

  14. +BZ)BZFT IUUQTJBNWFSZDPNn!JBNWFSZ

  15. None
  16. None
  17. 8FEFWFMPQ

  18. 8FUFBDI

  19. 8FXSJUF

  20. “I’m speaking at ElixirConf!”

  21. “That’s nice. Where is it?”

  22. “Orlando.”

  23. “DISNEY IS THERE!!! CAN WE GO?!”

  24. “Ya, actually it’s at Disney.”

  25. “OMG!!! YOU’RE SPEAKING AT DISNEY?!!”

  26. “Haha, sort of” *interrupted*

  27. *on phone* “YA! He’s speaking at Disney!! We’re GOING!!!” *squeal*

  28. None
  29. None
  30. Metaprogramming

  31. None
  32. programming Meta

  33. Working on Data

  34. data quantities on which operations are performed — Dictionary.app

  35. 4 + 2 data

  36. +(4, 2) data

  37. Kernel.+(4, 2)

  38. The program is data

  39. Macros

  40. Macros are everywhere

  41. good macros are invisible

  42. defmodule ItTest do use ExUnit.Case test “works” do assert It.works?

    end end macros
  43. Where is the data? Program

  44. defmodule ItTest do use ExUnit.Case test “works” do assert It.works?

    end end
  45. defmodule ItTest do use ExUnit.Case test “works” do assert It.works?

    end end
  46. defmodule ItTest do use ExUnit.Case test “works” do assert It.works?

    end end
  47. defmodule ItTest do use ExUnit.Case test “works” do assert It.works?

    end end
  48. Expression macro Expression ✨ ✨

  49. Expression?

  50. “Quoted” Elixir code

  51. {func, …, args}

  52. Abstract Syntax Tree

  53. + 1 - 2 3

  54. + 1 - 2 3 ( ( )) func Args

  55. 1 + ( 2 - 3 )

  56. Args are quoted

  57. Expression.match?(:lol > :wat) # => false Expression.match?(:lol = :wat) #

    => true
  58. defmacro match?({:=, _, _}) do true end defmacro match?(_expr) do

    false end
  59. Expression.match?(:lol > :wat) # => false Expression.match?(:lol = :wat) #

    => true
  60. false # => false true # => true

  61. Must return quoted

  62. defmacro badmatch do {:=, [], [:lol, :wat]} end

  63. defmacro badmatch do quote do :lol = :wat end end

  64. Expression.badmatch # (MatchError)

  65. :lol = :wat # (MatchError)

  66. Quote

  67. {func, meta, args}

  68. {:=, [], [:lol, :wat]}

  69. quote do :lol = :wat end

  70. Sort of like strings…

  71. “1+1”

  72. quote(do: 1+1)

  73. quote do 1+1 end

  74. Return AST of Expression

  75. 2 - 3 quote do end

  76. - 2 3 {: , […], [ , ]} (-

    2 3)
  77. quote do 1 + (2 - 3) end

  78. {: , […], [ , {: , […], [ ,

    ]}]} + 1 - 2 3 (+ 1 (- 2 3))
  79. Easier to grok

  80. {:+, […], [1, {:-, […], [2, 3]}]} quote do 1

    + (2 - 3) end
  81. Quoted Literals

  82. quote do: “foo” # “foo” quote do: :foo # :foo

    quote do: 1 # 1 quote do: [1] # [1] quote do: {1,2} # {1,2} quote do: [a: 1] # [a: 1]
  83. Unquote

  84. “Inject value in quote”

  85. Like string interpolation

  86. “Inject value into quoted string”

  87. name = “Sue” “hi name” # => “hi name”

  88. name = “Sue” “hi #{name}” # => “hi Sue”

  89. “Inject value into quoted Expression”

  90. num = 42 # => 42 quote do: 1 +

    num # => {:+, [], [1, {:num, [], :Elixir}]}
  91. num = 42 # => 42 quote do: 1 +

    unquote(num) # => {:+, [], [1, 42]}
  92. name = “Sue” “hi #{name}” # => “hi Sue”

  93. if

  94. if foo > 0 do IO.puts(“Pass!”) else IO.puts(“Fail.”) end

  95. if(…, do: …, else: …)

  96. if(…, [do: …, else: …]) cases cond

  97. Truthy & Falsey

  98. false || nil Falsey

  99. anything else Truthy

  100. case … do x when x in [false, nil] ->

    … _ -> … end cond cases
  101. defmacro if(c, do: tc, else: fc) do quote do case

    unquote(c) do x when x in [false, nil] -> unquote(fc) _ -> unquote(tc) end end end cond cases cond cases
  102. if do else end foo > 0 IO.puts(“Pass!”) IO.puts(“Fail.”)

  103. case do x when x in [false, nil] -> _

    -> end foo > 0 IO.puts(“Fail.”) IO.puts(“Pass!”)
  104. When to macro…

  105. #1 Don’t

  106. Write functions

  107. defmacro add(a, b) do quote do unquote(a) + unquote(b) end

    end
  108. def add(a, b) do a + b end

  109. More direct

  110. Less error-prone

  111. #2 Constants

  112. defmacro seconds_per_minute, do: 60 defmacro minutes_per_hour, do: 60

  113. #3 DSL

  114. html do body do div class: “content” do p “Engaging

    content.” end end end
  115. #4 Hide details

  116. test “works” do assert It.works? end

  117. #5 Reduce boilerplate

  118. Language extension

  119. defmodule Lolwat do def lol do :wat end end

  120. :elixir_module.compile(Lolwat, {:def, [context: Elixir, import: Kernel], [{:lol, [context: Elixir], Elixir},

    [do: :wat]]}, [], __ENV__)
  121. :elixir_module.compile(Lolwat, {:def, [context: Elixir, import: Kernel], [{:lol, [context: Elixir], Elixir},

    [do: :wat]]}, [], %{__struct__: Macro.Env, aliases: [], context: nil, context_modules: [Mex], export_vars: nil, file: "iex", function: nil, functions: [Mex: [expand_all: 2], "IEx.Helpers": [c: 1, c: 2, cd: 1, clear: 0, flush: 0, h: 0, i: 1, l: 1, ls: 0, ls: 1, nl: 1, nl: 2, pid: 1, pid: 3, pwd: 0, r: 1, recompile: 0, respawn: 0, v: 0, v: 1], Kernel: [!=: 2, !==: 2, *: 2, +: 1, +: 2, ++: 2, -: 1, -: 2, --: 2, /: 2, <: 2, <=: 2, ==: 2, ===: 2, =~: 2, >: 2, >=: 2, abs: 1, apply: 2, apply: 3, binary_part: 3, bit_size: 1, byte_size: 1, div: 2, elem: 2, exit: 1, function_exported?: 3, get_and_update_in: 3, get_in: 2, hd: 1, inspect: 1, inspect: 2, is_atom: 1, is_binary: 1, is_bitstring: 1, is_boolean: 1, is_float: 1, is_function: 1, is_function: 2, is_integer: 1, is_list: 1, is_map: 1, is_number: 1, is_pid: 1, is_port: 1, is_reference: 1, is_tuple: 1, length: 1, macro_exported?: 3, make_ref: 0, map_size: 1, max: 2, min: 2, node: 0, node: 1, not: 1, pop_in: 2, put_elem: 3, put_in: 3, rem: 2, round: 1, self: 0, send: 2, spawn: 1, spawn: 3, spawn_link: 1, spawn_link: 3, spawn_monitor: 1, spawn_monitor: 3, struct: 1, struct: 2, struct!: 1, struct!: 2, throw: 1, tl: 1, trunc: 1, tuple_size: 1, update_in: 3]], lexical_tracker: nil, line: 2, macro_aliases: [], macros: [Mex: [mex: 1], "IEx.Helpers": [b: 1, h: 1, import_file: 1, import_file: 2, import_file_if_available: 1, import_if_available: 1, import_if_available: 2, s: 1, t: 1], Kernel: [!: 1, &&: 2, ..: 2, <>: 2, @: 1, alias!: 1, and: 2, binding: 0, binding: 1, def: 1, def: 2, defdelegate: 2, defexception: 1, defimpl: 2, defimpl: 3, defmacro: 1, defmacro: 2, defmacrop: 1, defmacrop: 2, defmodule: 2, defoverridable: 1, defp: 1, defp: 2, defprotocol: 2, defstruct: 1, destructure: 2, get_and_update_in: 2, if: 2, in: 2, is_nil: 1, match?: 2, or: 2, pop_in: 1, put_in: 2, raise: 1, raise: 2, reraise: 2, reraise: 3, sigil_C: 2, sigil_D: 2, sigil_N: 2, sigil_R: 2, sigil_S: 2, sigil_T: 2, sigil_W: 2, sigil_c: 2, sigil_r: 2, sigil_s: 2, sigil_w: 2, to_char_list: 1, to_charlist: 1, to_string: 1, unless: 2, update_in: 2, use: 1, use: 2, var!: 1, var!: 2, |>: 2, ||: 2]], module: nil, requires: [IEx.Helpers, Kernel, Kernel.Typespec, Mex], vars: []})
  122. good macros are invisible

  123. CJUMZ&MJYJS.FUB

  124. !JBNWFSZ Questions?