├── .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 | ![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) 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 | ![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) 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 | --------------------------------------------------------------------------------