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: 2018-07-04
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 ### With
146
147 ```elixir
148 with {:ok, {int, _asdf}} <- Integer.parse("123asdf"),
149      {:ok, datetime, _utc_offset} <- DateTime.from_iso8601("2021-10-27T12:00:00Z") do
150   DateTime.add(datetime, int, :second)
151   # optional else clause. if not provided and an error occurs, the error is returned
152 else
153   :error -> "couldn't parse integer string"
154   {:error, :invalid_format} -> "couldn't parse date string"
155   _ -> "this will never get hit because all errors are handled"
156 end
157 ```
158
159 ### Errors
160
161 ```elixir
162 try do
163   throw(:hello)
164 catch
165   message -> "Got #{message}."
166 after
167   IO.puts("I'm the after clause.")
168 end
169 ```
170
171 ## Types
172
173 ### Primitives
174
175 | Sample                  | Type            |
176 | ---                     | ---             |
177 | `nil`                   | Nil/null        |
178 | `true` _/_ `false`      | Boolean         |
179 | ---                     | ---             |
180 | `?a`                    | Integer (ASCII) |
181 | `23`                    | Integer         |
182 | `3.14`                  | Float           |
183 | ---                     | ---             |
184 | `'hello'`               | Charlist        |
185 | `<<2, 3>>`              | Binary          |
186 | `"hello"`               | Binary string   |
187 | `:hello`                | Atom            |
188 | ---                     | ---             |
189 | `[a, b]`                | List            |
190 | `{a, b}`                | Tuple           |
191 | ---                     | ---             |
192 | `%{a: "hello"}`         | Map             |
193 | `%MyStruct{a: "hello"}` | Struct          |
194 | `fn -> ... end`         | Function        |
195
196 ### Type checks
197
198 ```elixir
199 is_atom/1
200 is_bitstring/1
201 is_boolean/1
202 is_function/1
203 is_function/2
204 is_integer/1
205 is_float/1
206 ```
207
208 ```elixir
209 is_binary/1
210 is_list/1
211 is_map/1
212 is_tuple/1
213 ```
214
215 ```elixir
216 is_nil/1
217 is_number/1
218 is_pid/1
219 is_port/1
220 is_reference/1
221 ```
222
223 ### Operators
224
225 ```elixir
226 left != right   # equal
227 left !== right  # match
228 left ++ right   # concat lists
229 left <> right   # concat string/binary
230 left =~ right   # regexp
231 ```
232
233 Modules
234 -------
235
236 ### Importing
237
238 ```elixir
239 require Redux   # compiles a module
240 import Redux    # compiles, and you can use without the `Redux.` prefix
241
242 use Redux       # compiles, and runs Redux.__using__/1
243 use Redux, async: true
244
245 import Redux, only: [duplicate: 2]
246 import Redux, only: :functions
247 import Redux, only: :macros
248
249 import Foo.{Bar, Baz}
250 ```
251
252 ### Aliases
253
254 ```elixir
255 alias Foo.Bar, as: Bar
256 alias Foo.Bar   # same as above
257
258 alias Foo.{Bar, Baz}
259 ```
260
261 ## String
262
263 ### Functions
264
265 ```elixir
266 import String
267 ```
268 {: .-setup}
269
270 ```elixir
271 str = "hello"
272 str |> length()        # → 5
273 str |> codepoints()    # → ["h", "e", "l", "l", "o"]
274 str |> slice(2..-1)    # → "llo"
275 str |> split(" ")      # → ["hello"]
276 str |> capitalize()    # → "Hello"
277 str |> match(regex)
278 ```
279
280 ### Inspecting objects
281
282 ```elixir
283 inspect(object, opts \\ [])
284 ```
285 ```elixir
286 value |> IO.inspect()
287 ```
288 ```elixir
289 value |> IO.inspect(label: "value")
290 ```
291
292 ## Numbers
293
294 ### Operations
295
296 ```elixir
297 abs(n)
298 round(n)
299 rem(a, b)   # remainder (modulo)
300 div(a, b)   # integer division
301 ```
302
303 ### Float
304
305 ```elixir
306 import Float
307 ```
308 {: .-setup}
309
310 ```elixir
311 n = 10.3
312 ```
313 {: .-setup}
314
315 ```elixir
316 n |> ceil()            # → 11.0
317 n |> ceil(2)           # → 11.30
318 n |> to_string()       # → "1.030000+e01"
319 n |> to_string([decimals: 2, compact: true])
320 ```
321
322 ```elixir
323 Float.parse("34")  # → { 34.0, "" }
324 ```
325
326 ### Integer
327
328 ```elixir
329 import Integer
330 ```
331 {: .-setup}
332
333 ```elixir
334 n = 12
335 ```
336 {: .-setup}
337
338 ```elixir
339 n |> digits()         # → [1, 2]
340 n |> to_charlist()    # → '12'
341 n |> to_string()      # → "12"
342 n |> is_even()
343 n |> is_odd()
344 ```
345
346 ```elixir
347 # Different base:
348 n |> digits(2)        # → [1, 1, 0, 0]
349 n |> to_charlist(2)   # → '1100'
350 n |> to_string(2)     # → "1100"
351 ```
352
353 ```elixir
354 parse("12")           # → {12, ""}
355 undigits([1, 2])      # → 12
356 ```
357
358 ### Type casting
359
360 ```elixir
361 Float.parse("34.1")    # → {34.1, ""}
362 Integer.parse("34")    # → {34, ""}
363 ```
364
365 ```elixir
366 Float.to_string(34.1)  # → "3.4100e+01"
367 Float.to_string(34.1, [decimals: 2, compact: true])  # → "34.1"
368 ```
369
370 ## Map
371
372 ### Defining
373
374 ```elixir
375 m = %{name: "hi"}       # atom keys (:name)
376 m = %{"name" => "hi"}   # string keys ("name")
377 ```
378
379 ### Updating
380
381 ```elixir
382 import Map
383 ```
384 {: .-setup}
385
386 ```elixir
387 m = %{m | name: "yo"}  # key must exist
388 ```
389
390 ```elixir
391 m |> put(:id, 2)      # → %{id: 2, name: "hi"}
392 m |> put_new(:id, 2)  # only if `id` doesn't exist (`||=`)
393 ```
394
395 ```elixir
396 m |> put(:b, "Banana")
397 m |> merge(%{b: "Banana"})
398 m |> update(:a, &(&1 + 1))
399 m |> update(:a, fun a -> a + 1 end)
400 ```
401
402 ```elixir
403 m |> get_and_update(:a, &(&1 || "default"))
404 # → {old, new}
405 ```
406
407 ### Deleting
408
409 ```elixir
410 m |> delete(:name)  # → %{}
411 m |> pop(:name)     # → {"John", %{}}
412 ```
413
414 ### Reading
415
416 ```elixir
417 m |> get(:id)       # → 1
418 m |> keys()         # → [:id, :name]
419 m |> values()       # → [1, "hi"]
420 ```
421
422 ```elixir
423 m |> to_list()      # → [id: 1, name: "hi"]
424                     # → [{:id, 1}, {:name, "hi"}]
425 ```
426
427 ### Deep
428
429 ```elixir
430 put_in(map, [:b, :c], "Banana")
431 put_in(map[:b][:c], "Banana")    # via macros
432 ```
433
434 ```elixir
435 get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
436 ```
437
438 ### Constructing from lists
439
440 ```elixir
441 Map.new([{:b, 1}, {:a, 2}])
442 Map.new([a: 1, b: 2])
443 Map.new([:a, :b], fn x -> {x, x} end)  # → %{a: :a, b: :b}
444 ```
445
446 ### Working with structs
447
448 #### Struct to map
449
450 ```elixir
451 Map.from_struct(%AnyStruct{a: "b"})  # → %{a: "b"}
452 ```
453
454 #### Map to struct
455
456 ```elixir
457 struct(AnyStruct, %{a: "b"})  # → %AnyStruct{a: "b"}
458 ```
459
460 ## List
461
462 ```elixir
463 import List
464 ```
465 {: .-setup}
466
467 ```elixir
468 l = [ 1, 2, 3, 4 ]
469 ```
470 {: .-setup}
471
472 ```elixir
473 l = l ++ [5]         # push (append)
474 l = [ 0 | list ]     # unshift (prepend)
475 ```
476
477 ```elixir
478 l |> first()
479 l |> last()
480 ```
481
482 ```elixir
483 l |> flatten()
484 l |> flatten(tail)
485 ```
486
487 Also see [Enum](#enum).
488
489
490 ## Enum
491
492 ### Usage
493
494 ```elixir
495 import Enum
496 ```
497 {: .-setup}
498
499 ```elixir
500 list = [:a, :b, :c]
501 ```
502 {: .-setup}
503
504 ```elixir
505 list |> at(0)         # → :a
506 list |> count()       # → 3
507 list |> empty?()      # → false
508 list |> any?()        # → true
509 ```
510
511 ```elixir
512 list |> concat([:d])  # → [:a, :b, :c, :d]
513 ```
514
515 Also, consider streams instead.
516
517 ### Map/reduce
518
519 ```elixir
520 list |> reduce(fn)
521 list |> reduce(acc, fn)
522 list |> map(fn)
523 list |> reject(fn)
524 list |> any?(fn)
525 list |> empty?(fn)
526 ```
527
528 ```elixir
529 [1, 2, 3, 4]
530 |> Enum.reduce(0, fn(x, acc) -> x + acc end)
531 ```
532
533 ## Tuple
534
535 ### Tuples
536
537 ```elixir
538 import Tuple
539 ```
540 {: .-setup}
541
542 ```elixir
543 t = { :a, :b }
544 ```
545
546 ```elixir
547 t |> elem(1)    # like tuple[1]
548 t |> put_elem(index, value)
549 t |> tuple_size()
550 ```
551
552 ### Keyword lists
553
554 ```elixir
555 list = [{ :name, "John" }, { :age, 15 }]
556 list[:name]
557 ```
558
559 ```elixir
560 # For string-keyed keyword lists
561 list = [{"size", 2}, {"type", "shoe"}]
562 List.keyfind(list, "size", 0)  # → {"size", 2}
563 ```
564
565 ## Functions
566
567 ### Lambdas
568
569 ```elixir
570 square = fn n -> n*n end
571 square.(20)
572 ```
573
574 ### & syntax
575
576 ```elixir
577 square = &(&1 * &1)
578 square.(20)
579
580 square = &Math.square/1
581 ```
582
583 ### Running
584
585 ```elixir
586 fun.(args)
587 apply(fun, args)
588 apply(module, fun, args)
589 ```
590
591 ### Function heads
592
593 ```elixir
594 def join(a, b \\ nil)
595 def join(a, b) when is_nil(b) do: a
596 def join(a, b) do: a <> b
597 ```
598
599 ## Structs
600
601 ### Structs
602
603 ```elixir
604 defmodule User do
605   defstruct name: "", age: nil
606 end
607
608 %User{name: "John", age: 20}
609
610 %User{}.struct  # → User
611 ```
612
613 See: [Structs](http://elixir-lang.org/getting-started/structs.html)
614
615 ## Protocols
616
617 ### Defining protocols
618
619 ```elixir
620 defprotocol Blank do
621   @doc "Returns true if data is considered blank/empty"
622   def blank?(data)
623 end
624 ```
625
626 ```elixir
627 defimpl Blank, for: List do
628   def blank?([]), do: true
629   def blank?(_), do: false
630 end
631
632 Blank.blank?([])  # → true
633 ```
634
635 ### Any
636
637 ```elixir
638 defimpl Blank, for: Any do ... end
639
640 defmodule User do
641   @derive Blank     # Falls back to Any
642   defstruct name: ""
643 end
644 ```
645
646 ### Examples
647
648 - `Enumerable` and `Enum.map()`
649 - `Inspect` and `inspect()`
650
651 ## Comprehensions
652
653 ### For
654
655 ```elixir
656 for n <- [1, 2, 3, 4], do: n * n
657 for n <- 1..4, do: n * n
658 ```
659
660 ```elixir
661 for {key, val} <- %{a: 10, b: 20}, do: val
662 # → [10, 20]
663 ```
664
665 ```elixir
666 for {key, val} <- %{a: 10, b: 20}, into: %{}, do: {key, val*val}
667 ```
668
669 ### Conditions
670
671 ```elixir
672 for n <- 1..10, rem(n, 2) == 0, do: n
673 # → [2, 4, 6, 8, 10]
674 ```
675
676 ### Complex
677
678 ```elixir
679 for dir <- dirs,
680     file <- File.ls!(dir),          # nested comprehension
681     path = Path.join(dir, file),    # invoked
682     File.regular?(path) do          # condition
683   IO.puts(file)
684 end
685 ```
686
687 ## Misc
688
689 ### Metaprogramming
690
691 ```elixir
692 __MODULE__
693 __MODULE__.__info__
694
695 @after_compile __MODULE__
696 def __before_compile__(env)
697 def __after_compile__(env, _bytecode)
698 def __using__(opts)    # invoked on `use`
699
700 @on_definition {__MODULE__, :on_def}
701 def on_def(_env, kind, name, args, guards, body)
702
703 @on_load :load_check
704 def load_check
705 ```
706
707 ### Regexp
708
709 ```elixir
710 exp = ~r/hello/
711 exp = ~r/hello/i
712 "hello world" =~ exp
713 ```
714
715 ### Sigils
716
717 ```elixir
718 ~r/regexp/
719 ~w(list of strings)
720 ~s|strings with #{interpolation} and \x20 escape codes|
721 ~S|no interpolation and no escapes|
722 ~c(charlist)
723 ```
724
725 Allowed chars: `/` `|` `"` `'` `(` `[` `{` `<` `"""`.
726 See: [Sigils](http://elixir-lang.org/getting-started/sigils.html)
727
728 ### Type specs
729
730 ```elixir
731 @spec round(number) :: integer
732
733 @type number_with_remark :: {number, String.t}
734 @spec add(number, number) :: number_with_remark
735 ```
736
737 Useful for [dialyzer](http://www.erlang.org/doc/man/dialyzer.html).
738 See: [Typespecs](http://elixir-lang.org/getting-started/typespecs-and-behaviours.html)
739
740 ### Behaviours
741
742 ```elixir
743 defmodule Parser do
744   @callback parse(String.t) :: any
745   @callback extensions() :: [String.t]
746 end
747 ```
748
749 ```elixir
750 defmodule JSONParser do
751   @behaviour Parser
752
753   def parse(str), do: # ... parse JSON
754   def extensions, do: ["json"]
755 end
756 ```
757
758 See: [Module](http://elixir-lang.org/docs/stable/elixir/Module.html)
759
760 ## References
761 {: .-one-column}
762
763 - [Learn Elixir in Y minutes](https://learnxinyminutes.com/docs/elixir/)