├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── README.md
├── README_zhTW.md
├── markdown.rb
└── mix.exs
/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | deps
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: ruby
4 |
5 | rvm:
6 | - 2.3.0
7 |
8 | install:
9 | - gem install mdl
10 |
11 | script:
12 | - mdl --style 'markdown.rb' README.md
13 | - mdl --style 'markdown.rb' CONTRIBUTING.md
14 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of
4 | fostering an open and welcoming community, we pledge to respect all people who
5 | contribute through reporting issues, posting feature requests, updating
6 | documentation, submitting pull requests or patches, and other activities.
7 |
8 | We are committed to making participation in this project a harassment-free
9 | experience for everyone, regardless of level of experience, gender, gender
10 | identity and expression, sexual orientation, disability, personal appearance,
11 | body size, race, ethnicity, age, religion, or nationality.
12 |
13 | Examples of unacceptable behavior by participants include:
14 |
15 | * The use of sexualized language or imagery
16 | * Personal attacks
17 | * Trolling or insulting/derogatory comments
18 | * Public or private harassment
19 | * Publishing other's private information, such as physical or electronic
20 | addresses, without explicit permission
21 | * Other unethical or unprofessional conduct
22 |
23 | Project maintainers have the right and responsibility to remove, edit, or
24 | reject comments, commits, code, wiki edits, issues, and other contributions
25 | that are not aligned to this Code of Conduct, or to ban temporarily or
26 | permanently any contributor for other behaviors that they deem inappropriate,
27 | threatening, offensive, or harmful.
28 |
29 | By adopting this Code of Conduct, project maintainers commit themselves to
30 | fairly and consistently applying these principles to every aspect of managing
31 | this project. Project maintainers who do not follow or enforce the Code of
32 | Conduct may be permanently removed from the project team.
33 |
34 | This Code of Conduct applies both within project spaces and in public spaces
35 | when an individual is representing the project or its community.
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
38 | reported by contacting a project maintainer at nessa.murmur@gmail.com. All
39 | complaints will be reviewed and investigated and will result in a response that
40 | is deemed necessary and appropriate to the circumstances. Maintainers are
41 | obligated to maintain confidentiality with regard to the reporter of an
42 | incident.
43 |
44 |
45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
46 | version 1.3.0, available at
47 | [http://contributor-covenant.org/version/1/3/0/][version]
48 |
49 | [homepage]: http://contributor-covenant.org
50 | [version]: http://contributor-covenant.org/version/1/3/0/
51 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the Elixir Style Guide
2 |
3 | First of all, thanks for wanting to contribute! :heart:
4 |
5 | You can contribute in several ways:
6 |
7 | * open up an issue if you find something plain old wrong in the style guide
8 | (e.g., typos or formatting);
9 | * open up an issue if you find inconsistencies in the style guide. This way, we
10 | can discuss the better way to eliminate those inconsistencies;
11 | * if you have any suggestions or opinions, open up an issue or (even better!) a
12 | pull request.
13 |
14 | If you edit the `README.md` file, please stick to this set of
15 | formatting/markup/style rules so that the style remains consistent:
16 |
17 | * don't make lines longer than 80 characters (most editors have an auto-wrapping
18 | functionality, for example [Emacs][Emacs LineWrap] or [Vim][Vim word wrap]);
19 | * use reference-style links, like `[an example][Example]`. Put the links in
20 | alphabetical order at the end of the document, and capitalize the first word
21 | of the link label.
22 |
23 | Install [Markdownlint] to check your changes, and run:
24 |
25 | ```sh
26 | mdl --style 'markdown.rb' README.md
27 | ```
28 |
29 | **IMPORTANT**: By submitting a patch, you agree that your work will be
30 | licensed under the license used by the project.
31 |
32 | ## Collaborators
33 |
34 | If you have contributed to the repository you can be appointed as a collaborator
35 | after submitting a change and getting it merged. Collaborators are invited to
36 | manage issues, make corrections to the style guide, review pull requests, and
37 | merge approved changes.
38 |
39 | 1. All changes must pass automatic checks before being merged.
40 | 1. Minor changes and corrections can be merged without review.
41 | 1. Significant changes or new style rules should be discussed and approved in a
42 | pull request.
43 |
44 |
45 | [Emacs LineWrap]: http://emacswiki.org/emacs/LineWrap
46 | [Markdownlint]: https://github.com/mivok/markdownlint
47 | [Vim word wrap]: http://vim.wikia.com/wiki/Automatic_word_wrapping
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [The Elixir Style Guide][Elixir Style Guide]
2 |
3 | ## Table of Contents
4 |
5 | * __[Prelude](#prelude)__
6 | * __[The Guide](#the-guide)__
7 | * [Source Code Layout](#source-code-layout)
8 | * [Syntax](#syntax)
9 | * [Naming](#naming)
10 | * [Comments](#comments)
11 | * [Comment Annotations](#comment-annotations)
12 | * [Modules](#modules)
13 | * [Documentation](#documentation)
14 | * [Typespecs](#typespecs)
15 | * [Structs](#structs)
16 | * [Exceptions](#exceptions)
17 | * _Collections_
18 | * [Strings](#strings)
19 | * _Regular Expressions_
20 | * [Metaprogramming](#metaprogramming)
21 | * [Testing](#testing)
22 | * [Suggested Alternatives](#suggested-alternatives)
23 | * [Style Guides](#style-guides)
24 | * [Tools](#tools)
25 | * __[Getting Involved](#getting-involved)__
26 | * [Contributing](#contributing)
27 | * [Spread the Word](#spread-the-word)
28 | * __[Copying](#copying)__
29 | * [License](#license)
30 | * [Attribution](#attribution)
31 |
32 | ## Prelude
33 |
34 | > Liquid architecture. It's like jazz — you improvise, you work together, you
35 | > play off each other, you make something, they make something.
36 | >
37 | > —Frank Gehry
38 |
39 | Style matters.
40 | [Elixir] has plenty of style but like all languages it can be stifled.
41 | Don't stifle the style.
42 |
43 | ## The Guide
44 |
45 | This is our attempt at starting a community style guide for the
46 | [Elixir programming language][Elixir].
47 | Please feel free to make pull requests and contribute.
48 | We really want Elixir to have as vibrant of a community as any language that's
49 | been around five times as long.
50 |
51 | If you're looking for other projects to contribute to please see the
52 | [Hex package manager site][Hex].
53 |
54 | ### Source Code Layout
55 |
56 |
57 |
58 | *
59 | Use two **spaces** per indentation level.
60 | No hard tabs.
61 | [[link](#spaces-indentation)]
62 |
63 | ```elixir
64 | # not preferred - four spaces
65 | def some_function do
66 | do_something
67 | end
68 |
69 | # preferred
70 | def some_function do
71 | do_something
72 | end
73 | ```
74 |
75 | *
76 | Use Unix-style line endings (\*BSD/Solaris/Linux/OSX users are covered by
77 | default, Windows users have to be extra careful).
78 | [[link](#line-endings)]
79 |
80 | *
81 | If you're using Git you might want to add the following configuration
82 | setting to protect your project from Windows line endings creeping in:
83 | [[link](#autocrlf)]
84 |
85 | ```sh
86 | git config --global core.autocrlf true
87 | ```
88 |
89 | *
90 | Use spaces around operators, after commas, colons and semicolons.
91 | Do not put spaces around matched pairs like brackets, parentheses, etc.
92 | Whitespace might be (mostly) irrelevant to the Elixir runtime, but its proper
93 | use is the key to writing easily readable code.
94 | [[link](#spaces)]
95 |
96 | ```elixir
97 | sum = 1 + 2
98 | {a, b} = {2, 3}
99 | [first | rest] = [1, 2, 3]
100 | Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts num end)
101 | ```
102 |
103 | *
104 | Do not use spaces after non-word operators that only take one argument; or
105 | around the range operator.
106 | [[link](#no-spaces)]
107 |
108 | ```elixir
109 | 0 - 1 == -1
110 | ^pinned = some_func()
111 | 5 in 1..10
112 | ```
113 |
114 | *
115 | Use blank lines between `def`s to break up a function into logical
116 | paragraphs.
117 | [[link](#def-spacing)]
118 |
119 | ```elixir
120 | def some_function(some_data) do
121 | altered_data = Module.function(data)
122 | end
123 |
124 | def some_function do
125 | result
126 | end
127 |
128 | def some_other_function do
129 | another_result
130 | end
131 |
132 | def a_longer_function do
133 | one
134 | two
135 |
136 | three
137 | four
138 | end
139 | ```
140 |
141 | *
142 | ...but run single-line `def`s that match for the same function together.
143 | [[link](#single-line-defs)]
144 |
145 | ```elixir
146 | def some_function(nil), do: {:err, "No Value"}
147 | def some_function([]), do: :ok
148 | def some_function([first | rest]) do
149 | some_function(rest)
150 | end
151 | ```
152 |
153 | *
154 | If you use the `do:` syntax with functions and the line that makes up the
155 | function body is long, put the `do:` on a new line indented one level more
156 | than the previous line.
157 | [[link](#long-dos)]
158 |
159 | ```elixir
160 | def some_function(args),
161 | do: Enum.map(args, fn(arg) -> arg <> " is on a very long line!" end)
162 | ```
163 |
164 | When you use the convention above and you have more than one function clause
165 | using the `do:` syntax, put the `do:` on a new line for each function clause:
166 |
167 | ```elixir
168 | # not preferred
169 | def some_function([]), do: :empty
170 | def some_function(_),
171 | do: :very_long_line_here
172 |
173 | # preferred
174 | def some_function([]),
175 | do: :empty
176 | def some_function(_),
177 | do: :very_long_line_here
178 | ```
179 |
180 | *
181 | If you have more than one multi-line `def`s do not use single-line `def`s.
182 | [[link](#multiple-function-defs)]
183 |
184 | ```elixir
185 | def some_function(nil) do
186 | {:err, "No Value"}
187 | end
188 |
189 | def some_function([]) do
190 | :ok
191 | end
192 |
193 | def some_function([first | rest]) do
194 | some_function(rest)
195 | end
196 |
197 | def some_function([first | rest], opts) do
198 | some_function(rest, opts)
199 | end
200 | ```
201 |
202 | *
203 | Use the pipe operator (`|>`) to chain functions together.
204 | [[link](#pipe-operator)]
205 |
206 | ```elixir
207 | # not preferred
208 | String.strip(String.downcase(some_string))
209 |
210 | # preferred
211 | some_string |> String.downcase |> String.strip
212 |
213 | # Multiline pipelines are not further indented
214 | some_string
215 | |> String.downcase
216 | |> String.strip
217 |
218 | # Multiline pipelines on the right side of a pattern match
219 | # should be indented on a new line
220 | sanitized_string =
221 | some_string
222 | |> String.downcase
223 | |> String.strip
224 | ```
225 |
226 | While this is the preferred method, take into account that copy-pasting
227 | multiline pipelines into IEx might result in a syntax error, as IEx will
228 | evaluate the first line without realizing that the next line has a pipeline.
229 |
230 | *
231 | Avoid using the pipe operator just once.
232 | [[link](#avoid-single-pipelines)]
233 |
234 | ```elixir
235 | # not preferred
236 | some_string |> String.downcase
237 |
238 | # preferred
239 | String.downcase(some_string)
240 | ```
241 |
242 | *
243 | Use _bare_ variables in the first part of a function chain.
244 | [[link](#bare-variables)]
245 |
246 | ```elixir
247 | # THE WORST!
248 | # This actually parses as String.strip("nope" |> String.downcase).
249 | String.strip "nope" |> String.downcase
250 |
251 | # not preferred
252 | String.strip(some_string) |> String.downcase |> String.codepoints
253 |
254 | # preferred
255 | some_string |> String.strip |> String.downcase |> String.codepoints
256 | ```
257 |
258 | *
259 | Avoid trailing whitespace.
260 | [[link](#trailing-whitespace)]
261 |
262 | *
263 | End each file with a newline.
264 | [[link](#newline-eof)]
265 |
266 | ### Syntax
267 |
268 | *
269 | Use parentheses when a `def` has arguments, and omit them when it doesn't.
270 | [[link](#parentheses)]
271 |
272 | ```elixir
273 | # not preferred
274 | def some_function arg1, arg2 do
275 | # body omitted
276 | end
277 |
278 | def some_function() do
279 | # body omitted
280 | end
281 |
282 | # preferred
283 | def some_function(arg1, arg2) do
284 | # body omitted
285 | end
286 |
287 | def some_function do
288 | # body omitted
289 | end
290 | ```
291 |
292 | *
293 | Never use `do:` for multi-line `if/unless`.
294 | [[link](#do-with-multi-line-if-unless)]
295 |
296 | ```elixir
297 | # not preferred
298 | if some_condition, do:
299 | # a line of code
300 | # another line of code
301 | # note no end in this block
302 |
303 | # preferred
304 | if some_condition do
305 | # some
306 | # lines
307 | # of code
308 | end
309 | ```
310 |
311 | *
312 | Use `do:` for single line `if/unless` statements.
313 | [[link](#do-with-single-line-if-unless)]
314 |
315 | ```elixir
316 | # preferred
317 | if some_condition, do: # some_stuff
318 | ```
319 |
320 | *
321 | Never use `unless` with `else`.
322 | Rewrite these with the positive case first.
323 | [[link](#unless-with-else)]
324 |
325 | ```elixir
326 | # not preferred
327 | unless success? do
328 | IO.puts 'failure'
329 | else
330 | IO.puts 'success'
331 | end
332 |
333 | # preferred
334 | if success? do
335 | IO.puts 'success'
336 | else
337 | IO.puts 'failure'
338 | end
339 | ```
340 |
341 | *
342 | Always use `true` as the last condition of a `cond` statement.
343 | [[link](#true-as-last-condition)]
344 |
345 | ```elixir
346 | cond do
347 | 1 + 2 == 5 ->
348 | "Nope"
349 | 1 + 3 == 5 ->
350 | "Uh, uh"
351 | true ->
352 | "OK"
353 | end
354 | ```
355 |
356 | *
357 | Never put a space between a function name and the opening parenthesis.
358 | [[link](#function-names-with-parentheses)]
359 |
360 | ```elixir
361 | # not preferred
362 | f (3 + 2) + 1
363 |
364 | # preferred
365 | f(3 + 2) + 1
366 | ```
367 |
368 | *
369 | Use parentheses in function calls, especially inside a pipeline.
370 | [[link](#function-calls-and-parentheses)]
371 |
372 | ```elixir
373 | # not preferred
374 | f 3
375 |
376 | # preferred
377 | f(3)
378 |
379 | # not preferred and parses as rem(2, (3 |> g)), which is not what you want.
380 | 2 |> rem 3 |> g
381 |
382 | # preferred
383 | 2 |> rem(3) |> g
384 | ```
385 |
386 | *
387 | Omit parentheses in macro calls when a do block is passed.
388 | [[link](#macro-calls-and-parentheses)]
389 |
390 | ```elixir
391 | # not preferred
392 | quote(do
393 | foo
394 | end)
395 |
396 | # preferred
397 | quote do
398 | foo
399 | end
400 | ```
401 |
402 | *
403 | Optionally omit parentheses in function calls (outside a pipeline) when the
404 | last argument is a function expression.
405 | [[link](#parentheses-and-function-expressions)]
406 |
407 | ```elixir
408 | # preferred
409 | Enum.reduce(1..10, 0, fn x, acc ->
410 | x + acc
411 | end)
412 |
413 | # also preferred
414 | Enum.reduce 1..10, 0, fn x, acc ->
415 | x + acc
416 | end
417 | ```
418 |
419 | *
420 | Use parentheses for calls to functions with zero arity, so they can be
421 | distinguished from variables.
422 | [[link](#parentheses-and-functions-with-zero-arity)]
423 |
424 | ```elixir
425 | defp do_stuff, do: ...
426 |
427 | # not preferred
428 | def my_func do
429 | do_stuff # is this a variable or a function call
430 | end
431 |
432 | # preferred
433 | def my_func do
434 | do_stuff() # this is clearly a function call
435 | end
436 | ```
437 |
438 | *
439 | Indent and align successive `with` clauses.
440 | Put the `do:` argument on a new line, indented normally.
441 | [[link](#with-clauses)]
442 |
443 | ```elixir
444 | with {:ok, foo} <- fetch(opts, :foo),
445 | {:ok, bar} <- fetch(opts, :bar),
446 | do: {:ok, foo, bar}
447 | ```
448 |
449 | *
450 | If the `with` expression has a `do` block with more than one line, or has an
451 | `else` option, use multiline syntax.
452 | [[link](#with-else)]
453 |
454 | ```elixir
455 | with {:ok, foo} <- fetch(opts, :foo),
456 | {:ok, bar} <- fetch(opts, :bar) do
457 | {:ok, foo, bar}
458 | else
459 | :error ->
460 | {:error, :bad_arg}
461 | end
462 | ```
463 |
464 | ### Naming
465 |
466 | *
467 | Use `snake_case` for atoms, functions and variables.
468 | [[link](#snake-case)]
469 |
470 | ```elixir
471 | # not preferred
472 | :"some atom"
473 | :SomeAtom
474 | :someAtom
475 |
476 | someVar = 5
477 |
478 | def someFunction do
479 | ...
480 | end
481 |
482 | def SomeFunction do
483 | ...
484 | end
485 |
486 | # preferred
487 | :some_atom
488 |
489 | some_var = 5
490 |
491 | def some_function do
492 | ...
493 | end
494 | ```
495 |
496 | *
497 | Use `CamelCase` for modules (keep acronyms like HTTP, RFC, XML uppercase).
498 | [[link](#camel-case)]
499 |
500 | ```elixir
501 | # not preferred
502 | defmodule Somemodule do
503 | ...
504 | end
505 |
506 | defmodule Some_Module do
507 | ...
508 | end
509 |
510 | defmodule SomeXml do
511 | ...
512 | end
513 |
514 | # preferred
515 | defmodule SomeModule do
516 | ...
517 | end
518 |
519 | defmodule SomeXML do
520 | ...
521 | end
522 | ```
523 |
524 | *
525 | The names of predicate macros (compile-time generated functions that return a
526 | boolean value) _that can be used within guards_ should be prefixed with `is_`.
527 | For a list of allowed expressions, see the [Guard][Guard Expressions] docs.
528 | [[link](#predicate-macro-names-with-guards)]
529 |
530 | ```elixir
531 | defmacro is_cool(var) do
532 | quote do: unquote(var) == "cool"
533 | end
534 | ```
535 |
536 | *
537 | The names of predicate functions _that cannot be used within guards_ should
538 | have a trailing question mark (`?`) rather than the `is_` (or similar) prefix.
539 | [[link](#predicate-macro-names-no-guards)]
540 |
541 | ```elixir
542 | def cool?(var) do
543 | # Complex check if var is cool not possible in a pure function.
544 | end
545 | ```
546 |
547 | *
548 | Private functions with the same name as public functions should start with
549 | `do_`.
550 | [[link](#private-functions-with-same-name-as-public)]
551 |
552 | ```elixir
553 | def sum(list), do: do_sum(list, 0)
554 |
555 | # private functions
556 | defp do_sum([], total), do: total
557 | defp do_sum([head | tail], total), do: do_sum(tail, head + total)
558 | ```
559 |
560 | ### Comments
561 |
562 | *
563 | Write expressive code and try to convey your program's intention through
564 | control-flow, structure and naming.
565 | [[link](#expressive-code)]
566 |
567 | *
568 | Use one space between the leading `#` character of the comment and the text of
569 | the comment.
570 | [[link](#comment-leading-spaces)]
571 |
572 | *
573 | Comments longer than a word are capitalized and use punctuation.
574 | Use [one space][Sentence Spacing] after periods.
575 | [[link](#comment-spacing)]
576 |
577 | ```elixir
578 | # not preferred
579 | String.upcase(some_string) # Capitalize string.
580 | ```
581 |
582 | #### Comment Annotations
583 |
584 | *
585 | Annotations should usually be written on the line immediately above the
586 | relevant code.
587 | [[link](#annotations)]
588 |
589 | *
590 | The annotation keyword is followed by a colon and a space, then a note
591 | describing the problem.
592 | [[link](#annotation-keyword)]
593 |
594 | *
595 | If multiple lines are required to describe the problem, subsequent lines
596 | should be indented two spaces after the `#`.
597 | [[link](#multiple-line-annotations)]
598 |
599 | *
600 | In cases where the problem is so obvious that any documentation would be
601 | redundant, annotations may be left at the end of the offending line with no
602 | note.
603 | This usage should be the exception and not the rule.
604 | [[link](#exceptions-to-annotations)]
605 |
606 | *
607 | Use `TODO` to note missing features or functionality that should be added at a
608 | later date.
609 | [[link](#todo-notes)]
610 |
611 | *
612 | Use `FIXME` to note broken code that needs to be fixed.
613 | [[link](#fixme-notes)]
614 |
615 | *
616 | Use `OPTIMIZE` to note slow or inefficient code that may cause performance
617 | problems.
618 | [[link](#optimize-notes)]
619 |
620 | *
621 | Use `HACK` to note code smells where questionable coding practices were used
622 | and should be refactored away.
623 | [[link](#hack-notes)]
624 |
625 | *
626 | Use `REVIEW` to note anything that should be looked at to confirm it is
627 | working as intended.
628 | For example: `REVIEW: Are we sure this is how the client does X currently?`
629 | [[link](#review-notes)]
630 |
631 | *
632 | Use other custom annotation keywords if it feels appropriate, but be sure to
633 | document them in your project's `README` or similar.
634 | [[link](#custom-keywords)]
635 |
636 | ### Modules
637 |
638 | *
639 | Use one module per file unless the module is only used internally by another
640 | module (such as a test).
641 | [[link](#one-module-per-file)]
642 |
643 | *
644 | Use underscored file names for `CamelCase` module names.
645 | [[link](#underscored-filenames)]
646 |
647 | ```elixir
648 | # file is called some_module.ex
649 |
650 | defmodule SomeModule do
651 | end
652 | ```
653 |
654 | *
655 | Represent each level of nesting within a module name as a directory.
656 | [[link](#module-name-nesting)]
657 |
658 | ```elixir
659 | # file is called parser/core/xml_parser.ex
660 |
661 | defmodule Parser.Core.XMLParser do
662 | end
663 | ```
664 |
665 | *
666 | Don't put a blank line after `defmodule`.
667 | [[link](#defmodule-spacing)]
668 |
669 | *
670 | Put a blank line after module-level code blocks.
671 | [[link](#module-block-spacing)]
672 |
673 | *
674 | List module attributes and directives in the following order:
675 | [[link](#module-attribute-ordering)]
676 |
677 | 1. `@moduledoc`
678 | 1. `@behaviour`
679 | 1. `use`
680 | 1. `import`
681 | 1. `alias`
682 | 1. `require`
683 | 1. `defstruct`
684 | 1. `@type`
685 | 1. `@module_attribute`
686 |
687 | Add a blank line between each grouping, and sort the terms (like module names)
688 | alphabetically.
689 | Here's an overall example of how you should order things in your modules:
690 |
691 | ```elixir
692 | defmodule MyModule do
693 | @moduledoc """
694 | An example module
695 | """
696 |
697 | @behaviour MyBehaviour
698 |
699 | use GenServer
700 |
701 | import Something
702 | import SomethingElse
703 |
704 | alias My.Long.Module.Name
705 | alias My.Other.Module.Name
706 |
707 | require Integer
708 |
709 | defstruct name: nil, params: []
710 |
711 | @type params :: [{binary, binary}]
712 |
713 | @module_attribute :foo
714 | @other_attribute 100
715 |
716 | ...
717 | end
718 | ```
719 |
720 | *
721 | Use the `__MODULE__` pseudo variable when a module refers to itself. This
722 | avoids having to update any self-references when the module name changes.
723 | [[link](#module-pseudo-variable)]
724 |
725 | ```elixir
726 | defmodule SomeProject.SomeModule do
727 | defstruct [:name]
728 |
729 | def name(%__MODULE__{name: name}), do: name
730 | end
731 | ```
732 |
733 | *
734 | If you want a prettier name for a module self-reference, set up an alias.
735 | [[link](#alias-self-referencing-modules)]
736 |
737 | ```elixir
738 | defmodule SomeProject.SomeModule do
739 | alias __MODULE__, as: SomeModule
740 |
741 | defstruct [:name]
742 |
743 | def name(%SomeModule{name: name}), do: name
744 | end
745 | ```
746 |
747 | ### Documentation
748 |
749 | Documentation in Elixir (when read either in `iex` with `h` or generated with
750 | [ExDoc]) uses the [Module Attributes] `@moduledoc` and `@doc`.
751 |
752 | *
753 | Always include a `@moduledoc` attribute in the line right after `defmodule` in
754 | your module.
755 | [[link](#moduledocs)]
756 |
757 | ```elixir
758 | # not preferred
759 |
760 | defmodule SomeModule do
761 |
762 | @moduledoc """
763 | About the module
764 | """
765 | ...
766 | end
767 |
768 | defmodule AnotherModule do
769 | use SomeModule
770 | @moduledoc """
771 | About the module
772 | """
773 | ...
774 | end
775 |
776 | # preferred
777 |
778 | defmodule SomeModule do
779 | @moduledoc """
780 | About the module
781 | """
782 | ...
783 | end
784 | ```
785 |
786 | *
787 | Use `@moduledoc false` if you do not intend on documenting the module.
788 | [[link](#moduledoc-false)]
789 |
790 | ```elixir
791 | defmodule SomeModule do
792 | @moduledoc false
793 | ...
794 | end
795 | ```
796 |
797 | *
798 | Separate code after the `@moduledoc` with a blank line.
799 | [[link](#moduledoc-spacing)]
800 |
801 | ```elixir
802 | # not preferred
803 |
804 | defmodule SomeModule do
805 | @moduledoc """
806 | About the module
807 | """
808 | use AnotherModule
809 | end
810 |
811 | # preferred
812 | defmodule SomeModule do
813 | @moduledoc """
814 | About the module
815 | """
816 |
817 | use AnotherModule
818 | end
819 | ```
820 |
821 | *
822 | Use heredocs with markdown for documentation.
823 | [[link](#heredocs)]
824 |
825 | ```elixir
826 | # not preferred
827 |
828 | defmodule SomeModule do
829 | @moduledoc "About the module"
830 | end
831 |
832 | defmodule SomeModule do
833 | @moduledoc """
834 | About the module
835 |
836 | Examples:
837 | iex> SomeModule.some_function
838 | :result
839 | """
840 | end
841 |
842 | # preferred
843 | defmodule SomeModule do
844 | @moduledoc """
845 | About the module
846 |
847 | ## Examples
848 |
849 | iex> SomeModule.some_function
850 | :result
851 | """
852 | end
853 | ```
854 |
855 | ### Typespecs
856 |
857 | Typespecs are notation for declaring types and specifications, for
858 | documentation or for the static analysis tool Dialyzer.
859 |
860 | Custom types should be defined at the top of the module with the other
861 | directives (see [Modules](#modules)).
862 |
863 | *
864 | Place `@typedoc` and `@type` definitions together, and separate each
865 | pair with a blank line.
866 | [[link](#typedocs)]
867 |
868 | ```elixir
869 | defmodule SomeModule do
870 | @moduledoc false
871 |
872 | @typedoc "The name"
873 | @type name :: atom
874 |
875 | @typedoc "The result"
876 | @type result :: {:ok, term} | {:error, term}
877 |
878 | ...
879 | end
880 | ```
881 |
882 | *
883 | If a union type is too long to fit on a single line, add a newline
884 | and indent with spaces to keep the return types aligned.
885 | [[link](#union-types)]
886 |
887 | ```elixir
888 | # not preferred - no indentation
889 | @type long_union_type :: some_type | another_type | some_other_type
890 | | a_final_type
891 |
892 | # preferred
893 | @type long_union_type :: some_type | another_type | some_other_type
894 | | a_final_type
895 |
896 | # also preferred - one return type per line
897 | @type long_union_type :: some_type
898 | | another_type
899 | | some_other_type
900 | | a_final_type
901 | ```
902 |
903 | *
904 | Name the main type for a module `t`, for example: the type specification for a
905 | struct.
906 | [[link](#naming-main-types)]
907 |
908 | ```elixir
909 | defstruct name: nil, params: []
910 |
911 | @type t :: %__MODULE__{
912 | name: String.t,
913 | params: Keyword.t
914 | }
915 | ```
916 |
917 | *
918 | Place specifications right before the function definition,
919 | without separating them by a blank line.
920 | [[link](#spec-spacing)]
921 |
922 | ```elixir
923 | @spec some_function(term) :: result
924 | def some_function(some_data) do
925 | {:ok, some_data}
926 | end
927 | ```
928 |
929 | ### Structs
930 |
931 | *
932 | If all the struct's fields default to nil, supply them as a list of atoms.
933 | [[link](#nil-struct-field-defaults)]
934 |
935 | ```elixir
936 | # not preferred
937 | defstruct name: nil, params: nil
938 |
939 | # preferred
940 | defstruct [:name, :params]
941 | ```
942 |
943 | *
944 | Indent additional lines of the struct definition, keeping the first keys
945 | aligned.
946 | [[link](#additional-struct-def-lines)]
947 |
948 | ```elixir
949 | defstruct foo: "test", bar: true, baz: false,
950 | qux: false, quux: nil
951 | ```
952 |
953 | ### Exceptions
954 |
955 | *
956 | Make exception names end with a trailing `Error`.
957 | [[link](#exception-names)]
958 |
959 | ```elixir
960 | # not preferred
961 | defmodule BadHTTPCode do
962 | defexception [:message]
963 | end
964 |
965 | defmodule BadHTTPCodeException do
966 | defexception [:message]
967 | end
968 |
969 | # preferred
970 | defmodule BadHTTPCodeError do
971 | defexception [:message]
972 | end
973 | ```
974 |
975 | *
976 | Use lowercase error messages when raising exceptions, with no trailing
977 | punctuation.
978 | [[link](#lowercase-error-messages)]
979 |
980 | ```elixir
981 | # not preferred
982 | raise ArgumentError, "This is not valid."
983 |
984 | # preferred
985 | raise ArgumentError, "this is not valid"
986 | ```
987 |
988 | ### Collections
989 |
990 | _No guidelines for collections have been added yet._
991 |
992 | ### Strings
993 |
994 | *
995 | Match strings using the string concatenator rather than binary patterns:
996 | [[link](#strings-matching-with-concatenator)]
997 |
998 | ```elixir
999 | # not preferred
1000 | <<"my"::utf8, _rest>> = "my string"
1001 |
1002 | # preferred
1003 | "my" <> _rest = "my string"
1004 | ```
1005 |
1006 | ### Regular Expressions
1007 |
1008 | _No guidelines for regular expressions have been added yet._
1009 |
1010 | ### Metaprogramming
1011 |
1012 | *
1013 | Avoid needless metaprogramming.
1014 | [[link](#avoid-metaprogramming)]
1015 |
1016 | ### Testing
1017 |
1018 | *
1019 | When writing [ExUnit] assertions, be consistent with the order of the expected
1020 | and actual values under testing.
1021 | Prefer placing the expected result on the right, unless the assertion is a
1022 | pattern match.
1023 | [[link](#testing-assert-order)]
1024 |
1025 | ```elixir
1026 | # preferred - expected result on the right
1027 | assert actual_function(1) == true
1028 | assert actual_function(2) == false
1029 |
1030 | # not preferred - inconsistent order
1031 | assert actual_function(1) == true
1032 | assert false == actual_function(2)
1033 |
1034 | # required - the assertion is a pattern match
1035 | assert {:ok, expected} = actual_function(3)
1036 | ```
1037 |
1038 | ### Suggested Alternatives
1039 |
1040 | Suggested alternatives are styles that haven't been seen much in the community
1041 | yet but might provide some value.
1042 |
1043 | #### Cond
1044 |
1045 | *
1046 | An atom can be used as a catch-all expression in a `cond` as it evaluates
1047 | to a truthy value.
1048 | Suggested atoms are `:else` or `:otherwise`
1049 | [[link](#atom-conditions)]
1050 |
1051 | ```elixir
1052 | cond do
1053 | 1 + 2 == 5 ->
1054 | "Nope"
1055 | 1 + 3 == 5 ->
1056 | "Uh, uh"
1057 | :else ->
1058 | "OK"
1059 | end
1060 |
1061 | # is the same as
1062 | cond do
1063 | 1 + 2 == 5 ->
1064 | "Nope"
1065 | 1 + 3 == 5 ->
1066 | "Uh, uh"
1067 | true ->
1068 | "OK"
1069 | end
1070 | ```
1071 |
1072 | ### Style Guides
1073 |
1074 | Check [Awesome Elixir][Style Guides] for a list of alternative style guides.
1075 |
1076 | ### Tools
1077 |
1078 | Refer to [Awesome Elixir][Code Analysis] for libraries and tools that can help
1079 | with code analysis and style linting.
1080 |
1081 | ## Getting Involved
1082 |
1083 | ### Contributing
1084 |
1085 | It's our hope that this will become a central hub for community discussion on
1086 | best practices in Elixir.
1087 | Feel free to open tickets or send pull requests with improvements.
1088 | Thanks in advance for your help!
1089 |
1090 | Check the [contributing guidelines](CONTRIBUTING.md)
1091 | and [code of conduct](CODE_OF_CONDUCT.md) for more information.
1092 |
1093 | ### Spread the Word
1094 |
1095 | A community style guide is meaningless without the community's support. Please
1096 | tweet, [star][Stargazers], and let any Elixir programmer know
1097 | about [this guide][Elixir Style Guide] so they can contribute.
1098 |
1099 | ## Copying
1100 |
1101 | ### License
1102 |
1103 | 
1104 | This work is licensed under a
1105 | [Creative Commons Attribution 3.0 Unported License][License]
1106 |
1107 | ### Attribution
1108 |
1109 | The structure of this guide, bits of example code, and many of the initial
1110 | points made in this document were borrowed from the [Ruby community style guide].
1111 | A lot of things were applicable to Elixir and allowed us to get _some_ document
1112 | out quicker to start the conversation.
1113 |
1114 | Here's the [list of people who has kindly contributed][Contributors] to this
1115 | project.
1116 |
1117 |
1118 | [Code Analysis]: https://github.com/h4cc/awesome-elixir#code-analysis
1119 | [Contributors]: https://github.com/christopheradams/elixir_style_guide/graphs/contributors
1120 | [Elixir Style Guide]: https://github.com/christopheradams/elixir_style_guide
1121 | [Elixir]: http://elixir-lang.org
1122 | [ExDoc]: https://github.com/elixir-lang/ex_doc
1123 | [ExUnit]: https://hexdocs.pm/ex_unit/ExUnit.html
1124 | [Guard Expressions]: http://elixir-lang.org/getting-started/case-cond-and-if.html#expressions-in-guard-clauses
1125 | [Hex]: https://hex.pm/packages
1126 | [License]: http://creativecommons.org/licenses/by/3.0/deed.en_US
1127 | [Module Attributes]: http://elixir-lang.org/getting-started/module-attributes.html#as-annotations
1128 | [Ruby community style guide]: https://github.com/bbatsov/ruby-style-guide
1129 | [Sentence Spacing]: http://en.wikipedia.org/wiki/Sentence_spacing
1130 | [Stargazers]: https://github.com/christopheradams/elixir_style_guide/stargazers
1131 | [Style Guides]: https://github.com/h4cc/awesome-elixir#styleguides
1132 |
--------------------------------------------------------------------------------
/README_zhTW.md:
--------------------------------------------------------------------------------
1 | # [The Elixir Style Guide][Elixir Style Guide]
2 |
3 | ## Table of Contents
4 |
5 | * __[前言](#前言)__
6 | * __[關於此指南](#關於此指南)__
7 | * [原始碼排板](#原始碼排版)
8 | * [語法](#語法)
9 | * [命名](#命名)
10 | * [註解](#註解)
11 | * [程式註釋](#程式註釋)
12 | * [模組](#模組)
13 | * [文件](#文件)
14 | * [型別](#型別)
15 | * [結構](#結構)
16 | * [例外](#例外)
17 | * _集合_
18 | * [字串](#字串)
19 | * _正規表示法_
20 | * [元編程](#metaprogramming)
21 | * [測試](#測試)
22 | * [Suggested Alternatives](#suggested-alternatives)
23 | * [條件式](#條件式)
24 | * [更多風格指南](#style-guides)
25 | * [Tools](#tools)
26 | * __[Getting Involved](#getting-involved)__
27 | * [Contributing](#contributing)
28 | * [Spread the Word](#spread-the-word)
29 | * __[Copying](#copying)__
30 | * [License](#license)
31 | * [Attribution](#attribution)
32 |
33 | ## 前言
34 |
35 | > Liquid architecture. It's like jazz — you improvise, you work together, you
36 | > play off each other, you make something, they make something.
37 | >
38 | > —Frank Gehry
39 |
40 | 程式的語法風格很重要!
41 | [Elixir] has plenty of style but like all languages it can be stifled.
42 | Don't stifle the style.
43 |
44 | ## 關於此指南
45 |
46 | 這個社群的風格指南嘗試提供一個社群維護的
47 | [Elixir 程式語言][Elixir] 的語法風格,
48 | 歡迎提出 Pull Request 來協助完善這份指南。
49 |
50 | 我們希望 Elixir 這個語言能夠像那些在它之前的語言一樣有個活躍的社群!
51 |
52 | 如果你想要找其他的 Project 來提出貢獻,請上
53 | [Hex package manager site][Hex]。
54 |
55 | ### 原始碼編
56 |
57 |
58 |
59 | *
60 | 請用兩個**空格**來縮排,
61 | 不要用 Hard Tab。
62 | [[link](#spaces-indentation)]
63 |
64 | ```elixir
65 | # 不好 - 四個空格
66 | def some_function do
67 | do_something
68 | end
69 |
70 | # 好
71 | def some_function do
72 | do_something
73 | end
74 | ```
75 |
76 | *
77 | 使用 Unix 風格的行編碼結尾 (預設包含 BSD/Solaris/Linux/OSX 的使用者,
78 | Windows 使用者要特別小心。)
79 | [[link](#line-endings)]
80 |
81 | *
82 | 如果你使用 Git ,你也許會想加入下面這個配置設定,
83 | 來保護你的專案被 Windows 的行編碼侵入:
84 | [[link](#autocrlf)]
85 |
86 | ```sh
87 | git config --global core.autocrlf true
88 | ```
89 |
90 | *
91 | 使用空格來圍繞運算子,在逗點 `,` 、冒號 `:` 及分號 `;` 之後,圍繞 `{` ,
92 | 和 `}` 之前。空格可能對(大部分)Elixir 直譯器來說是無關緊要的,
93 | 但正確的使用是寫出可讀性高的程式碼的關鍵。
94 | [[link](#spaces)]
95 |
96 | ```elixir
97 | sum = 1 + 2
98 | {a, b} = {2, 3}
99 | [first | rest] = [1, 2, 3]
100 | Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts num end)
101 | ```
102 |
103 | *
104 | 在單元運算子之後,或是範圍運算子的前後不要加空白。
105 | [[link](#no-spaces)]
106 |
107 | ```elixir
108 | 0 - 1 == -1
109 | ^pinned = some_func()
110 | 5 in 1..10
111 | ```
112 |
113 | *
114 | 在 `def` 之間使用空行,並且把方法分成合乎邏輯的段落。
115 | [[link](#def-spacing)]
116 |
117 | ```elixir
118 | def some_function(some_data) do
119 | altered_data = Module.function(data)
120 | end
121 |
122 | def some_function do
123 | result
124 | end
125 |
126 | def some_other_function do
127 | another_result
128 | end
129 |
130 | def a_longer_function do
131 | one
132 | two
133 |
134 | three
135 | four
136 | end
137 | ```
138 |
139 | *
140 | ...但在單行 `def` 時不要空行,並把相同的函式集中在一起。
141 | [[link](#single-line-defs)]
142 |
143 | ```elixir
144 | def some_function(nil), do: {:err, "No Value"}
145 | def some_function([]), do: :ok
146 | def some_function([first | rest]) do
147 | some_function(rest)
148 | end
149 | ```
150 |
151 | *
152 | 當你使用 `do:` 的語法宣告函式時,如果函式主體太長,請考慮把 `do:` 放在新的一
153 | 行,並縮排。
154 | [[link](#long-dos)]
155 |
156 | ```elixir
157 | def some_function(args),
158 | do: Enum.map(args, fn(arg) -> arg <> " is on a very long line!" end)
159 | ```
160 |
161 | 如果你使用了以上的風格,並且你同時用多個 `do:` 函式,請把所有的 `do:` 函式
162 | 主體都放在新的一行:
163 |
164 | ```elixir
165 | # 不好
166 | def some_function([]), do: :empty
167 | def some_function(_),
168 | do: :very_long_line_here
169 |
170 | # 好
171 | def some_function([]),
172 | do: :empty
173 | def some_function(_),
174 | do: :very_long_line_here
175 | ```
176 |
177 | *
178 | 如果你用了多行的 `def`,請不要再使用單行的 `def`。
179 | [[link](#multiple-function-defs)]
180 |
181 | ```elixir
182 | def some_function(nil) do
183 | {:err, "No Value"}
184 | end
185 |
186 | def some_function([]) do
187 | :ok
188 | end
189 |
190 | def some_function([first | rest]) do
191 | some_function(rest)
192 | end
193 |
194 | def some_function([first | rest], opts) do
195 | some_function(rest, opts)
196 | end
197 | ```
198 |
199 | *
200 | 請使用管線運算子(`|>`; pipe operator)鏈接多個函式。
201 | [[link](#pipe-operator)]
202 |
203 | ```elixir
204 | # 不好
205 | String.strip(String.downcase(some_string))
206 |
207 | # 好
208 | some_string |> String.downcase |> String.strip
209 |
210 | # 多行管線不用縮排
211 | some_string
212 | |> String.downcase
213 | |> String.strip
214 |
215 | # 如果多行管線用在模式比對(pattern match)的右側,請換行並縮排
216 | sanitized_string =
217 | some_string
218 | |> String.downcase
219 | |> String.strip
220 | ```
221 |
222 | 雖然這是推薦的寫法,務必記得在 IEx 中直接貼上多行管線時,很有可能會出錯。
223 | 這是因為 IEx 無法預知之後的管線的存在,所以在接到第一行程式後就立刻進行評估。
224 | 因此在 IEx 中,多行函式鏈接的管線運算元要寫在行尾。
225 |
226 | *
227 | 減少只使用一次的管線運算子。
228 | [[link](#avoid-single-pipelines)]
229 |
230 | ```elixir
231 | # 不好
232 | some_string |> String.downcase
233 |
234 | # 好
235 | String.downcase(some_string)
236 | ```
237 |
238 | *
239 | 把_單純_的變數放在函式鍊的最開頭。
240 | [[link](#bare-variables)]
241 |
242 | ```elixir
243 | # 非常不好!
244 | # 這會被編譯為 String.strip("nope" |> String.downcase)。
245 | String.strip "nope" |> String.downcase
246 |
247 | # 不好
248 | String.strip(some_string) |> String.downcase |> String.codepoints
249 |
250 | # 好
251 | some_string |> String.strip |> String.downcase |> String.codepoints
252 | ```
253 |
254 | *
255 | 避免行尾的空白(trailing whitespace)。
256 | [[link](#trailing-whitespace)]
257 |
258 | *
259 | 用新的一行來結尾每一個檔案。
260 | [[link](#newline-eof)]
261 |
262 | ### 語法
263 |
264 | *
265 | Use parentheses when a `def` has arguments, and omit them when it doesn't.
266 | 如果 `def` 有參數請用括號括起,如果沒有參數請不要使用括號。
267 | [[link](#parentheses)]
268 |
269 | ```elixir
270 | # 不好
271 | def some_function arg1, arg2 do
272 | # 省略
273 | end
274 |
275 | def some_function() do
276 | # 省略
277 | end
278 |
279 | # 好
280 | def some_function(arg1, arg2) do
281 | # 省略
282 | end
283 |
284 | def some_function do
285 | # 省略
286 | end
287 | ```
288 |
289 | *
290 | Never use `do:` for multi-line `if/unless`.
291 | 多行 `if/unless` 時,不要使用 `do:`
292 | [[link](#do-with-multi-line-if-unless)]
293 |
294 | ```elixir
295 | # 不好
296 | if some_condition, do:
297 | # 一行程式碼
298 | # 又一行程式碼
299 | # note 這個程式主體沒有結束
300 |
301 | # 好
302 | if some_condition do
303 | # 幾
304 | # 行
305 | # 程式碼
306 | end
307 | ```
308 |
309 | *
310 | Use `do:` for single line `if/unless` statements.
311 | 單行 `if/unless` 請用 `do:`。
312 | [[link](#do-with-single-line-if-unless)]
313 |
314 | ```elixir
315 | # 好
316 | if some_condition, do: # some_stuff
317 | ```
318 |
319 | *
320 | 如果用 `unless` **絕對** 不要用 `else`,
321 | 請將它們改寫成肯定條件。
322 | [[link](#unless-with-else)]
323 |
324 | ```elixir
325 | # 不好
326 | unless success? do
327 | IO.puts 'failure'
328 | else
329 | IO.puts 'success'
330 | end
331 |
332 | # 好
333 | if success? do
334 | IO.puts 'success'
335 | else
336 | IO.puts 'failure'
337 | end
338 | ```
339 |
340 | *
341 | `cond` 的最後一個條件一定是 `true`。
342 | [[link](#true-as-last-condition)]
343 |
344 | ```elixir
345 | cond do
346 | 1 + 2 == 5 ->
347 | "Nope"
348 | 1 + 3 == 5 ->
349 | "Uh, uh"
350 | true ->
351 | "OK"
352 | end
353 | ```
354 |
355 | *
356 | 不要在函式名與左括號後之間使用空白。
357 | [[link](#function-names-with-parentheses)]
358 |
359 | ```elixir
360 | # 不好
361 | f (3 + 2) + 1
362 |
363 | # 好
364 | f(3 + 2) + 1
365 | ```
366 |
367 | *
368 | 在使用函式時使用括號,特別是用在管線鍊時。
369 | [[link](#function-calls-and-parentheses)]
370 |
371 | ```elixir
372 | # 不好
373 | f 3
374 |
375 | # 好
376 | f(3)
377 |
378 | # 不好,此方法解讀為 rem(2, (3 |> g)),這應該不是你想要的。
379 | 2 |> rem 3 |> g
380 |
381 | # 好
382 | 2 |> rem(3) |> g
383 | ```
384 |
385 | *
386 | 在使用 `quote` 編輯巨集時,不要使用括號在 `do` 區塊之外。
387 | [[link](#macro-calls-and-parentheses)]
388 |
389 | ```elixir
390 | # 不好
391 | quote(do
392 | foo
393 | end)
394 |
395 | # 好
396 | quote do
397 | foo
398 | end
399 | ```
400 |
401 | *
402 | 如果函式在管線之外並且最後一個參數為函式時,可以選擇性的省略括號。
403 | [[link](#parentheses-and-function-expressions)]
404 |
405 | ```elixir
406 | # 好
407 | Enum.reduce(1..10, 0, fn x, acc ->
408 | x + acc
409 | end)
410 |
411 | # 也好
412 | Enum.reduce 1..10, 0, fn x, acc ->
413 | x + acc
414 | end
415 | ```
416 |
417 | *
418 | 當呼叫無參數的函式時,加上括號以便與變數區分。
419 | [[link](#parentheses-and-functions-with-zero-arity)]
420 |
421 | ```elixir
422 | defp do_stuff, do: ...
423 |
424 | # 不好
425 | def my_func do
426 | do_stuff # 這是變數還是函式呼叫?
427 | end
428 |
429 | # 好
430 | def my_func do
431 | do_stuff() # 這很明確是一個函式
432 | end
433 | ```
434 |
435 | *
436 | 利用縮排來排列每個 `with` 條件。
437 | 把 `do:` 的參數放在新的一行,正常的縮排。
438 | [[link](#with-clauses)]
439 |
440 | ```elixir
441 | with {:ok, foo} <- fetch(opts, :foo),
442 | {:ok, bar} <- fetch(opts, :bar),
443 | do: {:ok, foo, bar}
444 | ```
445 |
446 | *
447 | 如果 `with` 表達式使用了多行的 `do` 主體或是使用了 `else`,請使用多行語法。
448 | [[link](#with-else)]
449 |
450 | ```elixir
451 | with {:ok, foo} <- fetch(opts, :foo),
452 | {:ok, bar} <- fetch(opts, :bar) do
453 | {:ok, foo, bar}
454 | else
455 | :error ->
456 | {:error, :bad_arg}
457 | end
458 | ```
459 |
460 | ### 命名
461 |
462 | *
463 | 符號、方法與變數使用蛇底式小寫(snake_case)。
464 | [[link](#snake-case)]
465 |
466 | ```elixir
467 | # 不好
468 | :"some atom"
469 | :SomeAtom
470 | :someAtom
471 |
472 | someVar = 5
473 |
474 | def someFunction do
475 | ...
476 | end
477 |
478 | def SomeFunction do
479 | ...
480 | end
481 |
482 | # 好
483 | :some_atom
484 |
485 | some_var = 5
486 |
487 | def some_function do
488 | ...
489 | end
490 | ```
491 |
492 | *
493 | 模組使用駝峰式大小寫(CamelCase)。(保留像是 HTTP、RFC、XML 這種縮寫為大寫)
494 | [[link](#camel-case)]
495 |
496 | ```elixir
497 | # not preferred
498 | defmodule Somemodule do
499 | ...
500 | end
501 |
502 | defmodule Some_Module do
503 | ...
504 | end
505 |
506 | defmodule SomeXml do
507 | ...
508 | end
509 |
510 | # 好
511 | defmodule SomeModule do
512 | ...
513 | end
514 |
515 | defmodule SomeXML do
516 | ...
517 | end
518 | ```
519 |
520 | *
521 | _可以在 guard clause 使用的述語型巨集_(編譯產生的函示,回傳布林),請用 `is_`
522 | 為開頭。
523 | 允許的語法列表,請參考 [Guard][Guard Expressions] 文件。
524 | [[link](#predicate-macro-names-with-guards)]
525 |
526 | ```elixir
527 | defmacro is_cool(var) do
528 | quote do: unquote(var) == "cool"
529 | end
530 | ```
531 |
532 | *
533 | _無法在 guard clause 使用的巨集_請用問號(`?`),不要使用 `is_` 開頭。
534 | [[link](#predicate-macro-names-no-guards)]
535 |
536 | ```elixir
537 | def cool?(var) do
538 | # Complex check if var is cool not possible in a pure function.
539 | end
540 | ```
541 |
542 | *
543 | 與公開函數同名的私有函數請使用 `do_` 開頭。
544 | [[link](#private-functions-with-same-name-as-public)]
545 |
546 | ```elixir
547 | def sum(list), do: do_sum(list, 0)
548 |
549 | # 私有函數
550 | defp do_sum([], total), do: total
551 | defp do_sum([head | tail], total), do: do_sum(tail, head + total)
552 | ```
553 |
554 | ### 註解
555 |
556 | *
557 | 盡可能利用控制流、結構、和命名來表達你的程式的意圖。
558 | [[link](#expressive-code)]
559 |
560 | *
561 | 在註解的 `#` 與註解文字保留一空格。
562 | [[link](#comment-leading-spaces)]
563 |
564 | *
565 | 一個字以上的註釋需要使用正確的英文大寫與標點符號規則,並在句號後
566 | 加上一[空格][Sentence Spacing]。
567 | [[link](#comment-spacing)]
568 |
569 | ```elixir
570 | # 不好
571 | String.upcase(some_string) # Capitalize string.
572 | ```
573 |
574 | #### 程式註釋
575 |
576 | *
577 | 註釋請寫在相關程式碼的上一行。
578 | [[link](#annotations)]
579 |
580 | *
581 | 註釋關鍵字後方伴隨著一個冒號及空白,接著一個描述問題的記錄。
582 | [[link](#annotation-keyword)]
583 |
584 | *
585 | 如果需要用多行來描述問題,之後的行要放在 `#` 號後面並縮排兩個空格。
586 | [[link](#multiple-line-annotations)]
587 |
588 | *
589 | 在問題顯而易見並任何說明都是多餘的狀況下,註釋會被放在該程式碼的最後並不帶任何解釋。
590 | 這個用法是特例而不是規則。
591 | [[link](#exceptions-to-annotations)]
592 |
593 | *
594 | 使用 `TODO` 來標記之後應被加入的未實現功能或特色。
595 | [[link](#todo-notes)]
596 |
597 | *
598 | 使用 `FIXME` 來標記一個需要修復的程式碼。
599 | [[link](#fixme-notes)]
600 |
601 | *
602 | 使用 `OPTIMIZE` 來標記可能影響效能的緩慢或效率低落的程式碼。
603 | [[link](#optimize-notes)]
604 |
605 | *
606 | 使用 `HACK` 來標記代碼異味,其中包含了有問題的實作與及應該被重構的程式碼。
607 | [[link](#hack-notes)]
608 |
609 | *
610 | 使用 `REVIEW` 來標記任何需要審視及確認正常動作的地方。
611 | 舉例來說:`REVIEW: 我們確定用戶現在是這麼做的嗎?`
612 | [[link](#review-notes)]
613 |
614 | *
615 | 如果你覺得適當的話,使用其他你習慣的註釋關鍵字,但記得把它們記錄在專案的 `README` 或類似的地方。
616 | [[link](#custom-keywords)]
617 |
618 | ### 模組
619 |
620 | *
621 | Use one module per file unless the module is only used internally by another
622 | module (such as a test).
623 | 每一個檔案內只有一個模組,除非另一個模組只有被並存的模組使用(如測試)。
624 | [[link](#one-module-per-file)]
625 |
626 | *
627 | 使用小寫底線檔名(snake_case)配合駝峰式(CamelCase)模組名。
628 | [[link](#underscored-filenames)]
629 |
630 | ```elixir
631 | # 檔名: some_module.ex
632 |
633 | defmodule SomeModule do
634 | end
635 | ```
636 |
637 | *
638 | Represent each level of nesting within a module name as a directory.
639 | 用模組名中的階層來表示檔案位置。
640 | [[link](#module-name-nesting)]
641 |
642 | ```elixir
643 | # 檔案名為 parser/core/xml_parser.ex
644 |
645 | defmodule Parser.Core.XMLParser do
646 | end
647 | ```
648 |
649 | *
650 | 不要在 `defmodule` 後空行。
651 | [[link](#defmodule-spacing)]
652 |
653 | *
654 | 在模組區塊後空行。
655 | [[link](#module-block-spacing)]
656 |
657 | *
658 | 用下列順序來整理模組屬性:
659 | [[link](#module-attribute-ordering)]
660 |
661 | 1. `@moduledoc`
662 | 1. `@behaviour`
663 | 1. `use`
664 | 1. `import`
665 | 1. `alias`
666 | 1. `require`
667 | 1. `defstruct`
668 | 1. `@type`
669 | 1. `@module_attribute`
670 |
671 | 在每個屬性後加入空行,並依照字母順序整理。
672 | 以下為完整範例:
673 |
674 | ```elixir
675 | defmodule MyModule do
676 | @moduledoc """
677 | An example module
678 | """
679 |
680 | @behaviour MyBehaviour
681 |
682 | use GenServer
683 |
684 | import Something
685 | import SomethingElse
686 |
687 | alias My.Long.Module.Name
688 | alias My.Other.Module.Name
689 |
690 | require Integer
691 |
692 | defstruct name: nil, params: []
693 |
694 | @type params :: [{binary, binary}]
695 |
696 | @module_attribute :foo
697 | @other_attribute 100
698 |
699 | ...
700 | end
701 | ```
702 |
703 | *
704 | 當在模組內參考自己,請使用 `__MODULE__` 虛擬變數。如模組名修改,將不用另外
705 | 更新這些自我參考。
706 | [[link](#module-pseudo-variable)]
707 |
708 | ```elixir
709 | defmodule SomeProject.SomeModule do
710 | defstruct [:name]
711 |
712 | def name(%__MODULE__{name: name}), do: name
713 | end
714 | ```
715 |
716 | *
717 | 如果你想要比較美觀的自我參考,請使用 `alias`。
718 | [[link](#alias-self-referencing-modules)]
719 |
720 | ```elixir
721 | defmodule SomeProject.SomeModule do
722 | alias __MODULE__, as: SomeModule
723 |
724 | defstruct [:name]
725 |
726 | def name(%SomeModule{name: name}), do: name
727 | end
728 | ```
729 |
730 | ### 文件
731 |
732 | Elixir 的文件(當在 `iex` 的 `h` 指令或是用 [ExDoc] 產生)是指 `@moduledoc`
733 | 和 `@doc` 的[模組變數](Module Attributes)。
734 |
735 | *
736 | 在 `defmodule` 模組定義的下一行務必要是`@moduledoc` 模組變數。
737 | [[link](#moduledocs)]
738 |
739 | ```elixir
740 | # 不好
741 |
742 | defmodule SomeModule do
743 |
744 | @moduledoc """
745 | 關於模組
746 | """
747 | ...
748 | end
749 |
750 | defmodule AnotherModule do
751 | use SomeModule
752 | @moduledoc """
753 | 關於模組
754 | """
755 | ...
756 | end
757 |
758 | # 好
759 |
760 | defmodule SomeModule do
761 | @moduledoc """
762 | 關於模組
763 | """
764 | ...
765 | end
766 | ```
767 |
768 | *
769 | 使用 `@moduledoc false` 如果你不想為這個模組增加文件。
770 | [[link](#moduledoc-false)]
771 |
772 | ```elixir
773 | defmodule SomeModule do
774 | @moduledoc false
775 | ...
776 | end
777 | ```
778 |
779 | *
780 | 在 `@moduledoc` 後加一空行,與程式碼分開。
781 | [[link](#moduledoc-spacing)]
782 |
783 | ```elixir
784 | # 不好
785 |
786 | defmodule SomeModule do
787 | @moduledoc """
788 | 關於模組
789 | """
790 | use AnotherModule
791 | end
792 |
793 | # 好
794 | defmodule SomeModule do
795 | @moduledoc """
796 | 關於模組
797 | """
798 |
799 | use AnotherModule
800 | end
801 | ```
802 |
803 | *
804 | 在文件內使用 heredocs 和 markdown。
805 | [[link](#heredocs)]
806 |
807 | ```elixir
808 | # 不好
809 |
810 | defmodule SomeModule do
811 | @moduledoc "About the module"
812 | end
813 |
814 | defmodule SomeModule do
815 | @moduledoc """
816 | 關於模組
817 |
818 | Examples:
819 | iex> SomeModule.some_function
820 | :result
821 | """
822 | end
823 |
824 | # 好
825 | defmodule SomeModule do
826 | @moduledoc """
827 | 關於模組
828 |
829 | ## Examples
830 |
831 | iex> SomeModule.some_function
832 | :result
833 | """
834 | end
835 | ```
836 |
837 | ### 型別
838 |
839 | Typespecs 為宣告型別與規格,主要用於文件或是靜態分析工具如 Dialyzer。
840 |
841 | 自定義型別應該與其他指令放在模組上方(請見 [模組](#模組))。
842 |
843 | *
844 | 將 `@typedoc` 與 `@type` 宣告在一起,並每對之間用空行隔開。
845 | [[link](#typedocs)]
846 |
847 | ```elixir
848 | defmodule SomeModule do
849 | @moduledoc false
850 |
851 | @typedoc "The name"
852 | @type name :: atom
853 |
854 | @typedoc "The result"
855 | @type result :: {:ok, term} | {:error, term}
856 |
857 | ...
858 | end
859 | ```
860 |
861 | *
862 | 如果聯合型別 (union types) 宣告會超過一行,新的一行應用空格縮排。
863 | [[link](#union-types)]
864 |
865 | ```elixir
866 | # 不好 - 沒有縮排
867 | @type long_union_type :: some_type | another_type | some_other_type
868 | | a_final_type
869 |
870 | # 好
871 | @type long_union_type :: some_type | another_type | some_other_type
872 | | a_final_type
873 |
874 | # 也好 - 每個型別獨立成一行
875 | @type long_union_type :: some_type
876 | | another_type
877 | | some_other_type
878 | | a_final_type
879 | ```
880 |
881 | *
882 | 模組的主要型別命名為 `t`,範例:一個結構的型別。
883 | [[link](#naming-main-types)]
884 |
885 | ```elixir
886 | defstruct name: nil, params: []
887 |
888 | @type t :: %__MODULE__{
889 | name: String.t,
890 | params: Keyword.t
891 | }
892 | ```
893 |
894 | *
895 | 把函式型別宣告放在 `def` 的上一行,不需要空行。
896 | [[link](#spec-spacing)]
897 |
898 | ```elixir
899 | @spec some_function(term) :: result
900 | def some_function(some_data) do
901 | {:ok, some_data}
902 | end
903 | ```
904 |
905 | ### 結構
906 |
907 | *
908 | 如果此結構的所有欄位皆為空值,使用 atoms 的串列表示。
909 | [[link](#nil-struct-field-defaults)]
910 |
911 | ```elixir
912 | # 不好
913 | defstruct name: nil, params: nil
914 |
915 | # 好
916 | defstruct [:name, :params]
917 | ```
918 |
919 | *
920 | 如果結構宣告佔兩行以上,用空格縮排並對齊第一個 key。
921 | [[link](#additional-struct-def-lines)]
922 |
923 | ```elixir
924 | defstruct foo: "test", bar: true, baz: false,
925 | qux: false, quux: nil
926 | ```
927 |
928 | ### 例外
929 |
930 | *
931 | Make exception names end with a trailing `Error`.
932 | 自訂例外的結尾應為 `Error`。
933 | [[link](#exception-names)]
934 |
935 | ```elixir
936 | # 不好
937 | defmodule BadHTTPCode do
938 | defexception [:message]
939 | end
940 |
941 | defmodule BadHTTPCodeException do
942 | defexception [:message]
943 | end
944 |
945 | # 好
946 | defmodule BadHTTPCodeError do
947 | defexception [:message]
948 | end
949 | ```
950 |
951 | *
952 | 錯誤訊息使用小寫,而且不要在最後加上標點符號。
953 | [[link](#lowercase-error-messages)]
954 |
955 | ```elixir
956 | # 不好
957 | raise ArgumentError, "This is not valid."
958 |
959 | # 好
960 | raise ArgumentError, "this is not valid"
961 | ```
962 |
963 | ### 集合
964 |
965 | _No guidelines for collections have been added yet._
966 |
967 | ### 字串
968 |
969 | *
970 | 使用字串連接(string concatenatora)做模式比對,不要用二進制的模式。
971 | [[link](#strings-matching-with-concatenator)]
972 |
973 | ```elixir
974 | # 不好
975 | <<"my"::utf8, _rest>> = "my string"
976 |
977 | # 好
978 | "my" <> _rest = "my string"
979 | ```
980 |
981 | ### 正規表示法
982 |
983 | _No guidelines for regular expressions have been added yet._
984 |
985 | ### 元編程
986 |
987 | *
988 | 避免不必要的元編程(metaprogramming)。
989 | [[link](#avoid-metaprogramming)]
990 |
991 | ### 測試
992 |
993 | *
994 | 當在寫 [ExUnit] 斷言(assertions)時,保持預期值與實際值得一致性。
995 | 盡量把預期值放在右邊,除非此斷言是在進行模式比對。
996 | [[link](#testing-assert-order)]
997 |
998 | ```elixir
999 | # 好 - 期待值在右邊
1000 | assert actual_function(1) == true
1001 | assert actual_function(2) == false
1002 |
1003 | # 不好 - 順序交叉
1004 | assert actual_function(1) == true
1005 | assert false == actual_function(2)
1006 |
1007 | # 必須 - 斷言為模式比對
1008 | assert {:ok, expected} = actual_function(3)
1009 | ```
1010 |
1011 | ### 推薦的其它寫法
1012 |
1013 | 推薦的其它寫法是在社群中還不常見,但有其價值的編程風格。
1014 |
1015 | #### 條件式
1016 |
1017 | *
1018 | Atom 為真值(truthy),可作為 `cond` 條件式內全捕捉(catch-all)條件。
1019 | 建議使用 `:else` 或是 `:otherwise`。
1020 | [[link](#atom-conditions)]
1021 |
1022 | ```elixir
1023 | cond do
1024 | 1 + 2 == 5 ->
1025 | "Nope"
1026 | 1 + 3 == 5 ->
1027 | "Uh, uh"
1028 | :else ->
1029 | "OK"
1030 | end
1031 |
1032 | # 跟以下相同:
1033 |
1034 | cond do
1035 | 1 + 2 == 5 ->
1036 | "Nope"
1037 | 1 + 3 == 5 ->
1038 | "Uh, uh"
1039 | true ->
1040 | "OK"
1041 | end
1042 | ```
1043 |
1044 | ### 更多風格指南
1045 |
1046 | 到 [Awesome Elixir][Style Guides] 上找到更多的風格指南
1047 |
1048 | ### Tools
1049 |
1050 | Refer to [Awesome Elixir][Code Analysis] for libraries and tools that can help
1051 | with code analysis and style linting.
1052 |
1053 | ## Getting Involved
1054 |
1055 | ### Contributing
1056 |
1057 | It's our hope that this will become a central hub for community discussion on
1058 | best practices in Elixir.
1059 | Feel free to open tickets or send pull requests with improvements.
1060 | Thanks in advance for your help!
1061 |
1062 | Check the [contributing guidelines](CONTRIBUTING.md)
1063 | and [code of conduct](CODE_OF_CONDUCT.md) for more information.
1064 |
1065 | ### Spread the Word
1066 |
1067 | A community style guide is meaningless without the community's support. Please
1068 | tweet, [star][Stargazers], and let any Elixir programmer know
1069 | about [this guide][Elixir Style Guide] so they can contribute.
1070 |
1071 | ## Copying
1072 |
1073 | ### License
1074 |
1075 | 
1076 | This work is licensed under a
1077 | [Creative Commons Attribution 3.0 Unported License][License]
1078 |
1079 | ### Attribution
1080 |
1081 | The structure of this guide, bits of example code, and many of the initial
1082 | points made in this document were borrowed from the [Ruby community style guide].
1083 | A lot of things were applicable to Elixir and allowed us to get _some_ document
1084 | out quicker to start the conversation.
1085 |
1086 | Here's the [list of people who has kindly contributed][Contributors] to this
1087 | project.
1088 |
1089 |
1090 | [Code Analysis]: https://github.com/h4cc/awesome-elixir#code-analysis
1091 | [Contributors]: https://github.com/christopheradams/elixir_style_guide/graphs/contributors
1092 | [Elixir Style Guide]: https://github.com/christopheradams/elixir_style_guide
1093 | [Elixir]: http://elixir-lang.org
1094 | [ExDoc]: https://github.com/elixir-lang/ex_doc
1095 | [ExUnit]: https://hexdocs.pm/ex_unit/ExUnit.html
1096 | [Guard Expressions]: http://elixir-lang.org/getting-started/case-cond-and-if.html#expressions-in-guard-clauses
1097 | [Hex]: https://hex.pm/packages
1098 | [License]: http://creativecommons.org/licenses/by/3.0/deed.en_US
1099 | [Module Attributes]: http://elixir-lang.org/getting-started/module-attributes.html#as-annotations
1100 | [Ruby community style guide]: https://github.com/bbatsov/ruby-style-guide
1101 | [Sentence Spacing]: http://en.wikipedia.org/wiki/Sentence_spacing
1102 | [Stargazers]: https://github.com/christopheradams/elixir_style_guide/stargazers
1103 | [Style Guides]: https://github.com/h4cc/awesome-elixir#styleguides
1104 |
--------------------------------------------------------------------------------
/markdown.rb:
--------------------------------------------------------------------------------
1 | all
2 |
3 | exclude_rule 'MD036' # Emphasis used instead of a header
4 | exclude_rule 'MD033' # Inline HTML - exclude this to allow for anchor links in each bullet point
5 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule ElixirStyleGuide.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :elixir_style_guide,
6 | version: "0.1.0",
7 | elixir: "~> 1.0",
8 | build_embedded: Mix.env == :prod,
9 | start_permanent: Mix.env == :prod,
10 | deps: deps()]
11 | end
12 |
13 | def application do
14 | [applications: [:logger]]
15 | end
16 |
17 | defp deps do
18 | []
19 | end
20 | end
21 |
--------------------------------------------------------------------------------