OSDN Git Service

Regular updates
[twpd/master.git] / elixir.md
1 ---
2 title: Elixir
3 category: Elixir
4 layout: 2017/sheet
5 tags: [New]
6 updated: 2017-08-26
7 weight: -10
8 ---
9
10 ## Getting started
11 {: .-three-column}
12
13 ### Hello world
14 {: .-prime}
15
16 ```elixir
17 # hello.exs
18 defmodule Greeter do
19   def greet(name) do
20     message = "Hello, " <> name <> "!"
21     IO.puts message
22   end
23 end
24
25 Greeter.greet("world")
26 ```
27
28 ```bash
29 elixir hello.exs
30 # Hello, world!
31 ```
32 {: .-setup}
33
34 ### Variables
35
36 ```elixir
37 age = 23
38 ```
39
40 ### Maps
41
42 ```elixir
43 user = %{
44   name: "John",
45   city: "Melbourne"
46 }
47 ```
48
49 ```elixir
50 IO.puts "Hello, " <> user.name
51 ```
52 {: .-setup}
53
54 ### Lists
55
56 ```elixir
57 users = [ "Tom", "Dick", "Harry" ]
58 ```
59 {: data-line="1"}
60
61 ```elixir
62 Enum.map(users, fn user ->
63   IO.puts "Hello " <> user
64 end)
65 ```
66
67 ### Piping
68
69 ```elixir
70 source
71 |> transform(:hello)
72 |> print()
73 ```
74 {: data-line="2,3"}
75
76 ```elixir
77 # Same as:
78 print(transform(source, :hello))
79 ```
80
81 These two are equivalent.
82
83 ### Pattern matching
84
85 ```elixir
86 user = %{name: "Tom", age: 23}
87 %{name: username} = user
88 ```
89 {: data-line="2"}
90
91 This sets `username` to `"Tom"`.
92
93 ### Pattern matching in functions
94
95 ```elixir
96 def greet(%{name: username}) do
97   IO.puts "Hello, " <> username
98 end
99
100 user = %{name: "Tom", age: 23}
101 ```
102 {: data-line="1"}
103
104 Pattern matching works in function parameters too.
105
106 Control flow
107 ------------
108 {: .-three-column}
109
110 ### If
111
112 ```elixir
113 if false do
114   "This will never be seen"
115 else
116   "This will"
117 end
118 ```
119 ### Case
120
121 ```elixir
122 case {1, 2, 3} do
123   {4, 5, 6} ->
124     "This clause won't match"
125   {1, x, 3} ->
126     "This will match and bind x to 2"
127   _ ->
128    "This will match any value"
129 end
130 ```
131
132 ### Cond
133
134 ```elixir
135 cond do
136   1 + 1 == 3 ->
137     "I will never be seen"
138   2 * 5 == 12 ->
139     "Me neither"
140   true ->
141     "But I will (this is essentially an else)"
142 end
143 ```
144
145 ### Errors
146
147 ```elixir
148 try do
149   throw(:hello)
150 catch
151   message -> "Got #{message}."
152 after
153   IO.puts("I'm the after clause.")
154 end
155 ```
156
157 ## Types
158
159 ### Primitives
160
161 | Sample                  | Type            |
162 | ---                     | ---             |
163 | `nil`                   | Nil/null        |
164 | `true` _/_ `false`      | Boolean         |
165 | ---                     | ---             |
166 | `?a`                    | Integer (ASCII) |
167 | `23`                    | Integer         |
168 | `3.14`                  | Float           |
169 | ---                     | ---             |
170 | `'hello'`               | Charlist        |
171 | `<<2, 3>>`              | Binary          |
172 | `"hello"`               | Binary string   |
173 | `:hello`                | Atom            |
174 | ---                     | ---             |
175 | `[a, b]`                | List            |
176 | `{a, b}`                | Tuple           |
177 | ---                     | ---             |
178 | `%{a: "hello"}`         | Map             |
179 | `%MyStruct{a: "hello"}` | Struct          |
180 | `fn -> ... end`         | Function        |
181
182 ### Type checks
183
184 ```elixir
185 is_atom/1
186 is_bitstring/1
187 is_boolean/1
188 is_function/1
189 is_function/2
190 is_integer/1
191 is_float/1
192 ```
193
194 ```elixir
195 is_binary/1
196 is_list/1
197 is_map/1
198 is_tuple/1
199 ```
200
201 ```elixir
202 is_nil/1
203 is_number/1
204 is_pid/1
205 is_port/1
206 is_reference/1
207 ```
208
209 ### Operators
210
211 ```elixir
212 left != right   # equal
213 left !== right  # match
214 left ++ right   # concat lists
215 left <> right   # concat string/binary
216 left =~ right   # regexp
217 ```
218
219 Modules
220 -------
221
222 ### Importing
223
224 ```elixir
225 require Redux   # compiles a module
226 import Redux    # compiles, and you can use without the `Redux.` prefix
227
228 use Redux       # compiles, and runs Redux.__using__/1
229 use Redux, async: true
230
231 import Redux, only: [duplicate: 2]
232 import Redux, only: :functions
233 import Redux, only: :macros
234
235 import Foo.{Bar, Baz}
236 ```
237
238 ### Aliases
239
240 ```elixir
241 alias Foo.Bar, as: Bar
242 alias Foo.Bar   # same as above
243
244 alias Foo.{Bar, Baz}
245 ```
246
247 ## String
248
249 ### Functions
250
251 ```elixir
252 import String
253 ```
254 {: .-setup}
255
256 ```elixir
257 str = "hello"
258 str |> length()        # → 5
259 str |> codepoints()    # → ["h", "e", "l", "l", "o"]
260 str |> slice(2..-1)    # → "llo"
261 str |> split(" ")      # → ["hello"]
262 str |> capitalize()    # → "Hello"
263 str |> match(regex)
264 ```
265
266 ### Inspecting objects
267
268 ```elixir
269 inspect(object, opts \\ [])
270 ```
271 ```elixir
272 value |> IO.inspect()
273 ```
274 ```elixir
275 value |> IO.inspect(label: "value")
276 ```
277
278 ## Numbers
279
280 ### Operations
281
282 ```elixir
283 abs(n)
284 round(n)
285 rem(a, b)   # remainder (modulo)
286 div(a, b)   # integer division
287 ```
288
289 ### Float
290
291 ```elixir
292 import Float
293 ```
294 {: .-setup}
295
296 ```elixir
297 n = 10.3
298 ```
299 {: .-setup}
300
301 ```elixir
302 n |> ceil()            # → 11.0
303 n |> ceil(2)           # → 11.30
304 n |> to_string()       # → "1.030000+e01"
305 n |> to_string([decimals: 2, compact: true])
306 ```
307
308 ```elixir
309 Float.parse("34")  # → { 34.0, "" }
310 ```
311
312 ### Integer
313
314 ```elixir
315 import Integer
316 ```
317 {: .-setup}
318
319 ```elixir
320 n = 12
321 ```
322 {: .-setup}
323
324 ```elixir
325 n |> digits()         # → [1, 2]
326 n |> to_charlist()    # → '12'
327 n |> to_string()      # → "12"
328 n |> is_even()
329 n |> is_odd()
330 ```
331
332 ```elixir
333 # Different base:
334 n |> digits(2)        # → [1, 1, 0, 0]
335 n |> to_charlist(2)   # → '1100'
336 n |> to_string(2)     # → "1100"
337 ```
338
339 ```elixir
340 parse("12")           # → {12, ""}
341 undigits([1, 2])      # → 12
342 ```
343
344 ### Type casting
345
346 ```elixir
347 Float.parse("34.1")    # → {34.1, ""}
348 Integer.parse("34")    # → {34, ""}
349 ```
350
351 ```elixir
352 Float.to_string(34.1)  # → "3.4100e+01"
353 Float.to_string(34.1, [decimals: 2, compact: true])  # → "34.1"
354 ```
355
356 ## Map
357
358 ### Defining
359
360 ```elixir
361 m = %{name: "hi"}       # atom keys (:name)
362 m = %{"name" => "hi"}   # string keys ("name")
363 ```
364
365 ### Updating
366
367 ```elixir
368 import Map
369 ```
370 {: .-setup}
371
372 ```elixir
373 m = %{m | name: "yo"}  # key must exist
374 ```
375
376 ```elixir
377 m |> put(:id, 2)      # → %{id: 2, name: "hi"}
378 m |> put_new(:id, 2)  # only if `id` doesn't exist (`||=`)
379 ```
380
381 ```elixir
382 m |> put(:b, "Banana")
383 m |> merge(%{b: "Banana"})
384 m |> update(:a, &(&1 + 1))
385 m |> update(:a, fun a -> a + 1 end)
386 ```
387
388 ```elixir
389 m |> get_and_update(:a, &(&1 || "default"))
390 # → {old, new}
391 ```
392
393 ### Deleting
394
395 ```elixir
396 m |> delete(:name)  # → %{}
397 m |> pop(:name)     # → {"John", %{}}
398 ```
399
400 ### Reading
401
402 ```elixir
403 m |> get(:id)       # → 1
404 m |> keys()         # → [:id, :name]
405 m |> values()       # → [1, "hi"]
406 ```
407
408 ```elixir
409 m |> to_list()      # → [id: 1, name: "hi"]
410                     # → [{:id, 1}, {:name, "hi"}]
411 ```
412
413 ### Deep
414
415 ```elixir
416 put_in(map, [:b, :c], "Banana")
417 put_in(map[:b][:c], "Banana")    # via macros
418 ```
419
420 ```elixir
421 get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
422 ```
423
424 ### Constructing from lists
425
426 ```elixir
427 Map.new([{:b, 1}, {:a, 2}])
428 Map.new([a: 1, b: 2])
429 Map.new([:a, :b], fn x -> {x, x} end)  # → %{a: :a, b: :b}
430 ```
431
432 ## List
433
434 ```elixir
435 import List
436 ```
437 {: .-setup}
438
439 ```elixir
440 l = [ 1, 2, 3, 4 ]
441 ```
442 {: .-setup}
443
444 ```elixir
445 l = l ++ [5]         # push (append)
446 l = [ 0 | list ]     # unshift (prepend)
447 ```
448
449 ```elixir
450 l |> first()
451 l |> last()
452 ```
453
454 ```elixir
455 l |> flatten()
456 l |> flatten(tail)
457 ```
458
459 Also see [Enum](#enum).
460
461
462 ## Enum
463
464 ### Usage
465
466 ```elixir
467 import Enum
468 ```
469 {: .-setup}
470
471 ```elixir
472 list = [:a, :b, :c]
473 ```
474 {: .-setup}
475
476 ```elixir
477 list |> at(0)         # → :a
478 list |> count()       # → 3
479 list |> empty?()      # → false
480 list |> any?()        # → true
481 ```
482
483 ```elixir
484 list |> concat([:d])  # → [:a, :b, :c, :d]
485 ```
486
487 Also, consider streams instead.
488
489 ### Map/reduce
490
491 ```elixir
492 list |> reduce(fn)
493 list |> reduce(acc, fn)
494 list |> map(fn)
495 list |> reject(fn)
496 list |> any?(fn)
497 list |> empty?(fn)
498 ```
499
500 ```elixir
501 [1, 2, 3, 4]
502 |> Enum.reduce(0, fn(x, acc) -> x + acc end)
503 ```
504
505 ## Tuple
506
507 ### Tuples
508
509 ```elixir
510 import Tuple
511 ```
512 {: .-setup}
513
514 ```elixir
515 t = { :a, :b }
516 ```
517
518 ```elixir
519 t |> elem(1)    # like tuple[1]
520 t |> put_elem(index, value)
521 t |> tuple_size()
522 ```
523
524 ### Keyword lists
525
526 ```elixir
527 list = [{ :name, "John" }, { :age, 15 }]
528 list[:name]
529 ```
530
531 ```elixir
532 # For string-keyed keyword lists
533 list = [{"size", 2}, {"type", "shoe"}]
534 List.keyfind(list, "size", 0)  # → {"size", 2}
535 ```
536
537 ## Functions
538
539 ### Lambdas
540
541 ```elixir
542 square = fn n -> n*n end
543 square.(20)
544 ```
545
546 ### & syntax
547
548 ```elixir
549 square = &(&1 * &1)
550 square.(20)
551
552 square = &Math.square/1
553 ```
554
555 ### Running
556
557 ```elixir
558 fun.(args)
559 apply(fun, args)
560 apply(module, fun, args)
561 ```
562
563 ### Function heads
564
565 ```elixir
566 def join(a, b \\ nil)
567 def join(a, b) when is_nil(b) do: a
568 def join(a, b) do: a <> b
569 ```
570
571 ## Structs
572
573 ### Structs
574
575 ```elixir
576 defmodule User do
577   defstruct name: "", age: nil
578 end
579
580 %User{name: "John", age: 20}
581
582 %User{}.struct  # → User
583 ```
584
585 See: [Structs](http://elixir-lang.org/getting-started/structs.html)
586
587 ## Protocols
588
589 ### Defining protocols
590
591 ```elixir
592 defprotocol Blank do
593   @doc "Returns true if data is considered blank/empty"
594   def blank?(data)
595 end
596 ```
597
598 ```elixir
599 defimpl Blank, for: List do
600   def blank?([]), do: true
601   def blank?(_), do: false
602 end
603
604 Blank.blank?([])  # → true
605 ```
606
607 ### Any
608
609 ```elixir
610 defimpl Blank, for: Any do ... end
611
612 defmodule User do
613   @derive Blank     # Falls back to Any
614   defstruct name: ""
615 end
616 ```
617
618 ### Examples
619
620 - `Enumerable` and `Enum.map()`
621 - `Inspect` and `inspect()`
622
623 ## Comprehensions
624
625 ### For
626
627 ```elixir
628 for n <- [1, 2, 3, 4], do: n * n
629 for n <- 1..4, do: n * n
630 ```
631
632 ```elixir
633 for {key, val} <- %{a: 10, b: 20}, do: val
634 # → [10, 20]
635 ```
636
637 ```elixir
638 for {key, val} <- %{a: 10, b: 20}, into: %{}, do: {key, val*val}
639 ```
640
641 ### Conditions
642
643 ```elixir
644 for n <- 1..10, rem(n, 2) == 0, do: n
645 # → [2, 4, 6, 8, 10]
646 ```
647
648 ### Complex
649
650 ```elixir
651 for dir <- dirs,
652     file <- File.ls!(dir),          # nested comprehension
653     path = Path.join(dir, file),    # invoked
654     File.regular?(path) do          # condition
655   IO.puts(file)
656 end
657 ```
658
659 ## Misc
660
661 ### Metaprogramming
662
663 ```elixir
664 __MODULE__
665 __MODULE__.__info__
666
667 @after_compile __MODULE__
668 def __before_compile__(env)
669 def __after_compile__(env, _bytecode)
670 def __using__(opts)    # invoked on `use`
671
672 @on_definition {__MODULE__, :on_def}
673 def on_def(_env, kind, name, args, guards, body)
674
675 @on_load :load_check
676 def load_check
677 ```
678
679 ### Regexp
680
681 ```elixir
682 exp = ~r/hello/
683 exp = ~r/hello/i
684 "hello world" =~ exp
685 ```
686
687 ### Sigils
688
689 ```elixir
690 ~r/regexp/
691 ~w(list of strings)
692 ~s|strings with #{interpolation} and \x20 escape codes|
693 ~S|no interpolation and no escapes|
694 ~c(charlist)
695 ```
696
697 Allowed chars: `/` `|` `"` `'` `(` `[` `{` `<` `"""`.
698 See: [Sigils](http://elixir-lang.org/getting-started/sigils.html)
699
700 ### Type specs
701
702 ```elixir
703 @spec round(number) :: integer
704
705 @type number_with_remark :: {number, String.t}
706 @spec add(number, number) :: number_with_remark
707 ```
708
709 Useful for [dialyzer](http://www.erlang.org/doc/man/dialyzer.html).
710 See: [Typespecs](http://elixir-lang.org/getting-started/typespecs-and-behaviours.html)
711
712 ### Behaviours
713
714 ```elixir
715 defmodule Parser do
716   @callback parse(String.t) :: any
717   @callback extensions() :: [String.t]
718 end
719 ```
720
721 ```elixir
722 defmodule JSONParser do
723   @behaviour Parser
724
725   def parse(str), do: # ... parse JSON
726   def extensions, do: ["json"]
727 end
728 ```
729
730 See: [Module](http://elixir-lang.org/docs/stable/elixir/Module.html)
731
732 ## References
733 {: .-one-column}
734
735 - [Learn Elixir in Y minutes](https://learnxinyminutes.com/docs/elixir/)