├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── README-zhCN.md ├── README.md ├── Rakefile └── mix.exs /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | deps 3 | doc 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: ruby 4 | 5 | rvm: 6 | - 2.3.0 7 | 8 | install: 9 | - gem install mdl rake 10 | 11 | script: 12 | - rake test 13 | -------------------------------------------------------------------------------- /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 | Use Ruby and [Markdownlint] to check your changes: 24 | 25 | ```sh 26 | gem install mdl rake 27 | rake test 28 | ``` 29 | 30 | **IMPORTANT**: By submitting a patch, you agree that your work will be 31 | licensed under the license used by the project. 32 | 33 | ## The Project Board 34 | 35 | If you are looking for issues to work on, the [project board][Project KanBan] 36 | is the place to go. Usually, you look at the issues from right to left, as 37 | the ones in the rightmost part are the closer to get merged and have higher 38 | priority. 39 | 40 | If you just want to dive in and start writing, the backlog has the 41 | 'ready to be picked up' issues. These issues have been discussed already and 42 | are most likely just waiting for someone to make a PR. Just look for the 43 | issues with the `enhancement` and/or `help wanted` labels. 44 | 45 | ## Collaborators 46 | 47 | If you have contributed to the repository you can be appointed as a collaborator 48 | after submitting a change and getting it merged. Collaborators are invited to 49 | manage issues, make corrections to the style guide, review pull requests, and 50 | merge approved changes. 51 | 52 | 1. All changes must pass automatic checks before being merged. 53 | 1. Minor changes and corrections can be merged without review. 54 | 1. Significant changes or new style rules should be discussed and approved in a 55 | pull request. 56 | 57 | ## Translations 58 | 59 | If you would like to help translate the Style Guide, check if there is 60 | an [existing translation][Translations] to contribute to. To create a new 61 | translation: 62 | 63 | 1. [Fork] this repository. 64 | 1. Clone your fork locally. 65 | 1. Copy `README.md` to a new file named after the country/language, like 66 | `README-jaJP.md`, and commit your translations there. 67 | 1. Add the main [repo][Repo] as an upstream remote, to fetch and merge changes. 68 | 69 | 70 | [Emacs LineWrap]: http://emacswiki.org/emacs/LineWrap 71 | [Fork]: https://github.com/christopheradams/elixir_style_guide#fork-destination-box 72 | [Markdownlint]: https://github.com/mivok/markdownlint 73 | [Project KanBan]: https://github.com/christopheradams/elixir_style_guide/projects/1 74 | [Repo]: https://github.com/christopheradams/elixir_style_guide.git 75 | [Translations]: README.md#translations 76 | [Vim word wrap]: http://vim.wikia.com/wiki/Automatic_word_wrapping 77 | -------------------------------------------------------------------------------- /README-zhCN.md: -------------------------------------------------------------------------------- 1 | # [Elixir 风格指南][Elixir Style Guide] 2 | 3 | ## 目录 4 | 5 | * __[序幕](#序幕)__ 6 | * __[风格指南](#风格指南)__ 7 | * [组织](#组织) 8 | * [语法](#语法) 9 | * [命名](#命名) 10 | * [注释](#注释) 11 | * [注解](#注解) 12 | * [模块](#模块) 13 | * [文档](#文档) 14 | * [类型声明](#类型声明) 15 | * [结构](#结构) 16 | * [异常](#异常) 17 | * 集合 18 | * [字符串](#字符串) 19 | * 正则表达式 20 | * [元编程](#元编程) 21 | * [测试](#测试) 22 | * [更多风格指南](#更多风格指南) 23 | * [工具](#工具) 24 | * __[参与文档](#参与文档)__ 25 | * [贡献](#贡献) 26 | * [口耳相传](#口耳相传) 27 | * __[授权](#授权)__ 28 | * [协议](#协议) 29 | 30 | ## 序幕 31 | 32 | > 液体架构。像爵士乐一样 - 你们一起即兴演奏,互相回应着对方,你们在创作着音乐,他们在创作着音乐。 33 | > 34 | > —Frank Gehry 35 | 36 | 风格很重要。 37 | [Elixir] 有着大量的风格指南,但是像其他语言一样,这些指南都有可能被扼杀。 38 | 请不要扼杀这份指南。 39 | 40 | ## 风格指南 41 | 42 | 这是一份社群维护的 [Elixir 编程语言][Elixir] 风格指南。 43 | 44 | 欢迎提交 pull requests 和建议来完善这份指南,成为 Elixir 富有活力的社区的一员。 45 | 46 | 你可以在 [Hex 官网][Hex] 寻找其他的项目来贡献代码。 47 | 48 | ### 组织 49 | 50 | * 51 | 使用两个 **空格** 进行缩进,不要使用 Hard Tab。 52 | [[link](#spaces-indentation)] 53 | 54 | ```elixir 55 | # 不好 - 四个空格 56 | def some_function do 57 | do_something 58 | end 59 | 60 | # 好 61 | def some_function do 62 | do_something 63 | end 64 | ``` 65 | 66 | * 67 | 使用 Unix 风格换行符 (包括 \*BSD/Solaris/Linux/OSX 的用户, Windows 用户要特别小心)。 68 | [[link](#line-endings)] 69 | 70 | * 71 | 如果你使用 Git,可以使用下面的配置来避免 Windows 风格换行符: 72 | [[link](#autocrlf)] 73 | 74 | ```sh 75 | git config --global core.autocrlf true 76 | ``` 77 | 78 | * 79 | 在运算符的两侧添加空格,在逗号`,`,冒号`:`,分号`;`,之后添加空格。 80 | 不要在配对的括号两侧添加空格,例如,小括号`()`,大括号`{}`,等等。 81 | 空格一般来说对 (大部分) Elixir 编译器是无关紧要的,但是恰当的使用空格是写出可读性高的代码的关键。 82 | [[link](#spaces)] 83 | 84 | ```elixir 85 | sum = 1 + 2 86 | {a, b} = {2, 3} 87 | [first | rest] = [1, 2, 3] 88 | Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts num end) 89 | ``` 90 | 91 | * 92 | 在仅有一个参数的运算符之后,或者是范围运算符前后,不要添加空格。 93 | [[link](#no-spaces)] 94 | 95 | ```elixir 96 | 0 - 1 == -1 97 | ^pinned = some_func() 98 | 5 in 1..10 99 | ``` 100 | 101 | * 102 | 在 `def` 直接使用空行,并且把函数分成合乎逻辑的段落。 103 | [[link](#def-spacing)] 104 | 105 | ```elixir 106 | def some_function(some_data) do 107 | altered_data = Module.function(data) 108 | end 109 | 110 | def some_function do 111 | result 112 | end 113 | 114 | def some_other_function do 115 | another_result 116 | end 117 | 118 | def a_longer_function do 119 | one 120 | two 121 | 122 | three 123 | four 124 | end 125 | ``` 126 | 127 | * 128 | ...但是具有相同函数名的单行 `def` 写在一起。 129 | [[link](#single-line-defs)] 130 | 131 | ```elixir 132 | def some_function(nil), do: {:err, "No Value"} 133 | def some_function([]), do: :ok 134 | def some_function([first | rest]) do 135 | some_function(rest) 136 | end 137 | ``` 138 | 139 | * 140 | 当你使用 `do:` 的语法声明函数时,如果函数体太长,将 `do:` 放在新的一行,并且进行缩进。 141 | [[link](#long-dos)] 142 | 143 | ```elixir 144 | def some_function(args), 145 | do: Enum.map(args, fn(arg) -> arg <> " is on a very long line!" end) 146 | ``` 147 | 148 | 当你使用了上面的风格,并且同时 `do:` 声明多个函数子句时,请把所有的 `do:` 函数子句主体放在 149 | 150 | ```elixir 151 | # 不好 152 | def some_function([]), do: :empty 153 | def some_function(_), 154 | do: :very_long_line_here 155 | 156 | # 好 157 | def some_function([]), 158 | do: :empty 159 | def some_function(_), 160 | do: :very_long_line_here 161 | ``` 162 | 163 | * 164 | 如果你使用了多行的 `def`,请不要再使用单行的 `def`。 165 | [[link](#multiple-function-defs)] 166 | 167 | ```elixir 168 | def some_function(nil) do 169 | {:err, "No Value"} 170 | end 171 | 172 | def some_function([]) do 173 | :ok 174 | end 175 | 176 | def some_function([first | rest]) do 177 | some_function(rest) 178 | end 179 | 180 | def some_function([first | rest], opts) do 181 | some_function(rest, opts) 182 | end 183 | ``` 184 | 185 | * 186 | 使用管道运算符 (`|>`) 时,函数添加括号。 187 | [[link](#parentheses-pipe-operator)] 188 | 189 | ```elixir 190 | # 不好 191 | some_string |> String.downcase |> String.strip 192 | 193 | # 好 194 | some_string |> String.downcase() |> String.strip() 195 | ``` 196 | 197 | * 198 | 使用管道运算符链接多个函数。 199 | [[link](#pipe-operator)] 200 | 201 | ```elixir 202 | # 不好 203 | String.strip(String.downcase(some_string)) 204 | 205 | # 好 206 | some_string |> String.downcase() |> String.strip() 207 | 208 | # 多行管道不需要缩进 209 | some_string 210 | |> String.downcase() 211 | |> String.strip() 212 | 213 | # 多行管道在模式匹配的右侧要在下一行缩进 214 | sanitized_string = 215 | some_string 216 | |> String.downcase() 217 | |> String.strip() 218 | ``` 219 | 220 | 虽然这是推荐的写法,务必记得在 IEx 中直接粘贴多行管道时,可能会引起错误。这是由于 IEx 会 221 | 直接解释第一行,而不会意识到下一行管道的存在。 222 | 223 | * 224 | 避免只使用一次的管道。 225 | [[link](#avoid-single-pipelines)] 226 | 227 | ```elixir 228 | # 不好 229 | some_string |> String.downcase() 230 | 231 | # 好 232 | String.downcase(some_string) 233 | ``` 234 | 235 | * 236 | 237 | 把纯变量放在函数调用链的开头。 238 | 239 | [[link](#bare-variables)] 240 | 241 | ```elixir 242 | # 非常不好! 243 | # 这会被编译为 String.strip("nope" |> String.downcase()). 244 | String.strip "nope" |> String.downcase() 245 | 246 | # 不好 247 | String.strip(some_string) |> String.downcase() |> String.codepoints() 248 | 249 | # 好 250 | some_string |> String.strip() |> String.downcase() |> String.codepoints() 251 | ``` 252 | 253 | * 254 | 255 | 多行列表进行赋值时,另起一行,并且列表的元素要进行对齐。 256 | 257 | [[link](#multiline-list-assign)] 258 | 259 | ```elixir 260 | # 不好 - 没有缩进 261 | list = [:first_item, :second_item, :next_item, 262 | :last_item] 263 | 264 | # 好一点 - 进行缩进 265 | list = [:first_item, :second_item, :next_item, 266 | :last_item] 267 | 268 | # 好 - 列表另起一行 269 | # 适合更短,更紧凑的列表 270 | list = 271 | [:first_item, :second_item, :next_item, 272 | :last_item] 273 | 274 | # 同样很好 - 列表的每个元素另起一行 275 | # 适合长列表,长元素列表,或者有注释的列表 276 | list = [ 277 | :first_item, 278 | :second_item, 279 | :next_item, 280 | # comment 281 | :many_items, 282 | :last_item 283 | ] 284 | ``` 285 | 286 | * 287 | 避免行尾的空白 (trailing whitespace)。 288 | [[link](#trailing-whitespace)] 289 | 290 | * 291 | 292 | 用新的一行来结束源文件。 293 | 294 | [[link](#newline-eof)] 295 | 296 | ### 语法 297 | 298 | * 299 | 300 | 有参函数使用括号,否则省略括号。 301 | 302 | [[link](#parentheses)] 303 | 304 | ```elixir 305 | # 不好 306 | def some_function arg1, arg2 do 307 | # body omitted 308 | end 309 | 310 | def some_function() do 311 | # body omitted 312 | end 313 | 314 | # 好 315 | def some_function(arg1, arg2) do 316 | # body omitted 317 | end 318 | 319 | def some_function do 320 | # body omitted 321 | end 322 | ``` 323 | 324 | * 325 | 326 | 多行赋值后添加空行,表示赋值结束。 327 | 328 | [[link](#add-blank-line-after-multiline-assignment)] 329 | 330 | ```elixir 331 | # 不好 332 | some_string = 333 | "Hello" 334 | |> String.downcase() 335 | |> String.strip() 336 | another_string <> some_string 337 | 338 | # 好 339 | some_string = 340 | "Hello" 341 | |> String.downcase() 342 | |> String.strip() 343 | 344 | another_string <> some_string 345 | ``` 346 | 347 | ```elixir 348 | # 不好 349 | something = 350 | if x == 2 do 351 | "Hi" 352 | else 353 | "Bye" 354 | end 355 | String.downcase(something) 356 | 357 | # 好 358 | something = 359 | if x == 2 do 360 | "Hi" 361 | else 362 | "Bye" 363 | end 364 | 365 | String.downcase(something) 366 | ``` 367 | 368 | * 369 | 370 | 多行 `if/unless` 时,避免使用 `do:`。 371 | 372 | [[link](#do-with-multi-line-if-unless)] 373 | 374 | ```elixir 375 | # 不好 376 | if some_condition, do: 377 | # a line of code 378 | # another line of code 379 | # note no end in this block 380 | 381 | # 好 382 | if some_condition do 383 | # some 384 | # lines 385 | # of code 386 | end 387 | ``` 388 | 389 | * 390 | 391 | 单行 `if/unless` 时使用 `do:`。 392 | 393 | [[link](#do-with-single-line-if-unless)] 394 | 395 | ```elixir 396 | # 好 397 | if some_condition, do: # some_stuff 398 | ``` 399 | 400 | * 401 | 402 | 避免使用 `unless` 搭配 `else`,将它们改写为肯定条件。 403 | 404 | [[link](#unless-with-else)] 405 | 406 | ```elixir 407 | # 不好 408 | unless success? do 409 | IO.puts 'failure' 410 | else 411 | IO.puts 'success' 412 | end 413 | 414 | # 好 415 | if success? do 416 | IO.puts 'success' 417 | else 418 | IO.puts 'failure' 419 | end 420 | ``` 421 | 422 | * 423 | 424 | `cond` 的最后一个条件一定是 `true` 。 425 | 426 | [[link](#true-as-last-condition)] 427 | 428 | ```elixir 429 | # 不好 430 | cond do 431 | 1 + 2 == 5 -> 432 | "Nope" 433 | 1 + 3 == 5 -> 434 | "Uh, uh" 435 | :else -> 436 | "OK" 437 | end 438 | 439 | # 好 440 | cond do 441 | 1 + 2 == 5 -> 442 | "Nope" 443 | 1 + 3 == 5 -> 444 | "Uh, uh" 445 | true -> 446 | "OK" 447 | end 448 | ``` 449 | 450 | * 451 | 452 | 不要在函数名和左括号之间添加空格。 453 | 454 | [[link](#function-names-with-parentheses)] 455 | 456 | ```elixir 457 | # 不好 458 | f (3 + 2) + 1 459 | 460 | # 好 461 | f(3 + 2) + 1 462 | ``` 463 | 464 | * 465 | 466 | 函数调用时使用括号,特别在使用管道时。 467 | 468 | [[link](#function-calls-and-parentheses)] 469 | 470 | ```elixir 471 | # 不好 472 | f 3 473 | 474 | # 好 475 | f(3) 476 | 477 | # 不好 478 | 2 |> rem 3 |> g 479 | 480 | # 好 481 | 2 |> rem(3) |> g 482 | ``` 483 | 484 | * 485 | 486 | 当使用 `do` 块调用宏时,省略括号。 487 | 488 | [[link](#macro-calls-and-parentheses)] 489 | 490 | ```elixir 491 | # 不好 492 | quote(do 493 | foo 494 | end) 495 | 496 | # 好 497 | quote do 498 | foo 499 | end 500 | ``` 501 | 502 | * 503 | 504 | 当函数调用在管道之外,并且最后一个参数是函数表达式时,可以选择性的省略括号。 505 | 506 | [[link](#parentheses-and-function-expressions)] 507 | 508 | ```elixir 509 | # 好 510 | Enum.reduce(1..10, 0, fn x, acc -> 511 | x + acc 512 | end) 513 | 514 | # 同样好 515 | Enum.reduce 1..10, 0, fn x, acc -> 516 | x + acc 517 | end 518 | ``` 519 | 520 | * 521 | 522 | 无参函数调用时添加括号,以便和变量进行区分。 523 | 524 | 从 Elixir 1.4 开始,编译器会在有歧义的地方发出警告。 525 | 526 | [[link](#parentheses-and-functions-with-zero-arity)] 527 | 528 | ```elixir 529 | defp do_stuff, do: ... 530 | 531 | # 不好 532 | def my_func do 533 | do_stuff # 这是变量还是函数调用? 534 | end 535 | 536 | # 好用 537 | def my_func do 538 | do_stuff() # 这是一个明确的函数调用 539 | end 540 | ``` 541 | 542 | * 543 | 544 | 关键字列表总是使用特殊语法。 545 | 546 | [[link](#keyword-list-syntax)] 547 | 548 | ```elixir 549 | # 不好 550 | some_value = [{:a, "baz"}, {:b, "qux"}] 551 | 552 | # 好 553 | some_value = [a: "baz", b: "qux"] 554 | ``` 555 | 556 | * 557 | 558 | 当关键字列表的括号可选时则省略。 559 | 560 | [[link](#keyword-list-brackets)] 561 | 562 | ```elixir 563 | # 不好 564 | some_function(foo, bar, [a: "baz", b: "qux"]) 565 | 566 | # 好 567 | some_function(foo, bar, a: "baz", b: "qux") 568 | ``` 569 | 570 | * 571 | 572 | 缩排 `with` 的多个条件,`do` 的参数在新的一行正常缩进。 573 | 574 | [[link](#with-clauses)] 575 | 576 | ```elixir 577 | with {:ok, foo} <- fetch(opts, :foo), 578 | {:ok, bar} <- fetch(opts, :bar), 579 | do: {:ok, foo, bar} 580 | ``` 581 | 582 | * 583 | 584 | 如果 `with` 表达式 `do` 块超过了一行,或者使用了 `else`,请使用多行语法。 585 | 586 | [[link](#with-else)] 587 | 588 | ```elixir 589 | with {:ok, foo} <- fetch(opts, :foo), 590 | {:ok, bar} <- fetch(opts, :bar) do 591 | {:ok, foo, bar} 592 | else 593 | :error -> 594 | {:error, :bad_arg} 595 | end 596 | ``` 597 | 598 | ### 命名 599 | 600 | * 601 | 602 | 符号,方法,变量,使用蛇底式 (`snake_case`)。 603 | 604 | [[link](#snake-case)] 605 | 606 | ```elixir 607 | # 不好 608 | :"some atom" 609 | :SomeAtom 610 | :someAtom 611 | 612 | someVar = 5 613 | 614 | def someFunction do 615 | ... 616 | end 617 | 618 | def SomeFunction do 619 | ... 620 | end 621 | 622 | # 好 623 | :some_atom 624 | 625 | some_var = 5 626 | 627 | def some_function do 628 | ... 629 | end 630 | ``` 631 | 632 | * 633 | 634 | 模块名使用驼峰式 (`CamelCase`) (保留像是 HTTP, RFC, XML 这种缩写为大写形式)。 635 | 636 | [[link](#camel-case)] 637 | 638 | ```elixir 639 | # 不好 640 | defmodule Somemodule do 641 | ... 642 | end 643 | 644 | defmodule Some_Module do 645 | ... 646 | end 647 | 648 | defmodule SomeXml do 649 | ... 650 | end 651 | 652 | # 好 653 | defmodule SomeModule do 654 | ... 655 | end 656 | 657 | defmodule SomeXML do 658 | ... 659 | end 660 | ``` 661 | 662 | * 663 | 664 | 可以在 `guard clause` 中使用的谓词宏 (编译期生成返回布尔值的函数),命名应该使用 `is_` 前缀。 665 | 666 | 允许的表达式列表,请参考 [Guard][Guard Expressions] 文档。 667 | 668 | [[link](#predicate-macro-names-with-guards)] 669 | 670 | ```elixir 671 | defmacro is_cool(var) do 672 | quote do: unquote(var) == "cool" 673 | end 674 | ``` 675 | 676 | * 677 | 678 | 谓词函数,无法在 `guard clause` 中使用,命名时应该以 `?` 结尾,而不是 `is_` 作为前缀。 679 | 680 | [[link](#predicate-macro-names-no-guards)] 681 | 682 | ```elixir 683 | def cool?(var) do 684 | # Complex check if var is cool not possible in a pure function. 685 | end 686 | ``` 687 | 688 | * 689 | 690 | 当私有函数和公共函数具有相同的名称时,使用 `do_` 作为前缀。 691 | 692 | [[link](#private-functions-with-same-name-as-public)] 693 | 694 | ```elixir 695 | def sum(list), do: do_sum(list, 0) 696 | 697 | # private functions 698 | defp do_sum([], total), do: total 699 | defp do_sum([head | tail], total), do: do_sum(tail, head + total) 700 | ``` 701 | 702 | ### 注释 703 | 704 | * 705 | 706 | 编写富有表现力的代码,通过控制流,结构和命名来表达程序的意图。 707 | 708 | [[link](#expressive-code)] 709 | 710 | * 711 | 712 | 在注释的 `#` 之后,保留一个空格。 713 | 714 | [[link](#comment-leading-spaces)] 715 | 716 | ```elixir 717 | String.first(some_string) #不好 718 | String.first(some_string) # 好 719 | ``` 720 | 721 | * 722 | 723 | 一个字以上的注释需要使用正确的英文大小写以及标点符号,并且在句号后添加空格。 724 | 725 | [[link](#comment-grammar)] 726 | 727 | ```elixir 728 | # 不好 729 | # these lowercase comments are missing punctuation 730 | 731 | # 好 732 | # Capitalization example 733 | # Use punctuation for complete sentences. 734 | ``` 735 | 736 | #### 注解 737 | 738 | * 739 | 740 | 注解通常写在相关代码的上一行。 741 | 742 | [[link](#annotations)] 743 | 744 | * 745 | 746 | 注解关键字要大写,紧接着是一个冒号和一个空格,然后是问题的描述。 747 | 748 | [[link](#annotation-keyword)] 749 | 750 | ```elixir 751 | # TODO: Deprecate in v1.5. 752 | def some_function(arg), do: {:ok, arg} 753 | ``` 754 | 755 | * 756 | 757 | 在问题显而易见的情况下,任何说明都是多余的,注解要放到代码的最后并且不带任何解释。 758 | 759 | 这个用法是特例而不是规则。 760 | 761 | [[link](#exceptions-to-annotations)] 762 | 763 | ```elixir 764 | start_task() 765 | Process.sleep(5000) # FIXME 766 | ``` 767 | 768 | * 769 | 770 | 使用 `TODO` 来标记未来要实现功能或特性。 771 | 772 | [[link](#todo-notes)] 773 | 774 | * 775 | 776 | 使用 `FIXME` 来标记要被修复的代码。 777 | 778 | [[link](#fixme-notes)] 779 | 780 | * 781 | 782 | 使用 `OPTIMIZE` 来标记可能会引起性能问题的缓慢或者低效的代码。 783 | 784 | [[link](#optimize-notes)] 785 | 786 | * 787 | 788 | 使用 `HACK` 来比较代码中的坏味道,这些有问题的编码实践应当被重构。 789 | 790 | [[link](#hack-notes)] 791 | 792 | * 793 | 794 | 使用 `REVIEW` 来标记需要确认是否正常工作的地方。 795 | 796 | 例如: `REVIEW: Are we sure this is how the client does X currently?` 797 | [[link](#review-notes)] 798 | 799 | * 800 | 801 | 如果你觉得需要的话,使用其他自定义的关键字,并将它们记录到 `README` 或者其他类似的文档中。 802 | 803 | [[link](#custom-keywords)] 804 | 805 | ### 模块 806 | 807 | * 808 | 809 | 每个源文件内只有一个模块,除非模块只在另一个模块内部使用 (例如测试)。 810 | 811 | [[link](#one-module-per-file)] 812 | 813 | * 814 | 815 | 文件名使用 `蛇底式` (`snake_case`),模块名使用 `驼峰式` (`CamelCase`)。 816 | 817 | [[link](#underscored-filenames)] 818 | 819 | ```elixir 820 | # file is called some_module.ex 821 | 822 | defmodule SomeModule do 823 | end 824 | ``` 825 | 826 | * 827 | 828 | 嵌套模块命名中的每一层代表一层文件夹。 829 | 830 | [[link](#module-name-nesting)] 831 | 832 | ```elixir 833 | # file is called parser/core/xml_parser.ex 834 | 835 | defmodule Parser.Core.XMLParser do 836 | end 837 | ``` 838 | 839 | * 840 | 841 | `defmodule` 之后不要添加空行。 842 | 843 | [[link](#defmodule-spacing)] 844 | 845 | * 846 | 847 | 模块代码之后添加空行。 848 | 849 | [[link](#module-block-spacing)] 850 | 851 | * 852 | 853 | 模块属性和指令要按照下面的顺序: 854 | 855 | [[link](#module-attribute-ordering)] 856 | 857 | 1. `@moduledoc` 858 | 2. `@behaviour` 859 | 3. `use` 860 | 4. `import` 861 | 5. `alias` 862 | 6. `require` 863 | 7. `defstruct` 864 | 8. `@type` 865 | 9. `@module_attribute` 866 | 10. `@callback` 867 | 11. `@macrocallback` 868 | 12. `@optional_callbacks` 869 | 870 | 在每一组属性或者指令后加入空行,并且本组的项目 (例如,模块名) 要按照字母排序。 871 | 872 | 这里有一个完整的例子: 873 | 874 | ```elixir 875 | defmodule MyModule do 876 | @moduledoc """ 877 | An example module 878 | """ 879 | 880 | @behaviour MyBehaviour 881 | 882 | use GenServer 883 | 884 | import Something 885 | import SomethingElse 886 | 887 | alias My.Long.Module.Name 888 | alias My.Other.Module.Example 889 | 890 | require Integer 891 | 892 | defstruct name: nil, params: [] 893 | 894 | @type params :: [{binary, binary}] 895 | 896 | @module_attribute :foo 897 | @other_attribute 100 898 | 899 | @callback some_function(term) :: :ok | {:error, term} 900 | 901 | @macrocallback macro_name(term) :: Macro.t 902 | 903 | @optional_callbacks macro_name: 1 904 | 905 | ... 906 | end 907 | ``` 908 | 909 | * 910 | 当模块引用自身时使用 `__MODULE__` 伪变量。 911 | 912 | 如果模块名称修改,可以避免更新对模块自身的引用。 913 | 914 | [[link](#module-pseudo-variable)] 915 | 916 | ```elixir 917 | defmodule SomeProject.SomeModule do 918 | defstruct [:name] 919 | 920 | def name(%__MODULE__{name: name}), do: name 921 | end 922 | ``` 923 | 924 | * 925 | 926 | 设置一个别名,可以让模块名更具可读性。 927 | 928 | [[link](#alias-self-referencing-modules)] 929 | 930 | ```elixir 931 | defmodule SomeProject.SomeModule do 932 | alias __MODULE__, as: SomeModule 933 | 934 | defstruct [:name] 935 | 936 | def name(%SomeModule{name: name}), do: name 937 | end 938 | ``` 939 | 940 | * 941 | 避免在模块名和命名空间中使用重复的名称,这样提高可读性并且消除 [ambiguous aliases][Conflicting Aliases]。 942 | 943 | [[link](#repetitive-module-names)] 944 | 945 | ```elixir 946 | # 不好 947 | defmodule Todo.Todo do 948 | ... 949 | end 950 | 951 | # 好 952 | defmodule Todo.Item do 953 | ... 954 | end 955 | ``` 956 | 957 | ### 文档 958 | 959 | 使用模块变量 (Module Attributes) `@moduledoc` 和 `@doc` 声明文档 (在 `iex` 使用 `h`查看或者使用 [ExDoc] 生成)。 960 | 961 | * 962 | 963 | 在 `defmodule` 之后下一行总是定义 `@moduledoc` 变量。 964 | 965 | [[link](#moduledocs)] 966 | 967 | ```elixir 968 | # 不好 969 | 970 | defmodule SomeModule do 971 | 972 | @moduledoc """ 973 | About the module 974 | """ 975 | ... 976 | end 977 | 978 | defmodule AnotherModule do 979 | use SomeModule 980 | @moduledoc """ 981 | About the module 982 | """ 983 | ... 984 | end 985 | 986 | # 好 987 | 988 | defmodule SomeModule do 989 | @moduledoc """ 990 | About the module 991 | """ 992 | ... 993 | end 994 | ``` 995 | 996 | * 997 | 998 | 使用 `@moduledoc false`,如果你不想为这个模块增加文档。 999 | 1000 | [[link](#moduledoc-false)] 1001 | 1002 | ```elixir 1003 | defmodule SomeModule do 1004 | @moduledoc false 1005 | ... 1006 | end 1007 | ``` 1008 | 1009 | * 1010 | 1011 | `@moduledoc` 之后添加一个空行,与其他代码分开。 1012 | 1013 | [[link](#moduledoc-spacing)] 1014 | 1015 | ```elixir 1016 | # 不好 1017 | 1018 | defmodule SomeModule do 1019 | @moduledoc """ 1020 | About the module 1021 | """ 1022 | use AnotherModule 1023 | end 1024 | 1025 | # 好 1026 | defmodule SomeModule do 1027 | @moduledoc """ 1028 | About the module 1029 | """ 1030 | 1031 | use AnotherModule 1032 | end 1033 | ``` 1034 | 1035 | * 1036 | 1037 | 在文档内使用 `heredocs` 和 `markdown`。 1038 | 1039 | [[link](#heredocs)] 1040 | 1041 | ```elixir 1042 | # 不好 1043 | 1044 | defmodule SomeModule do 1045 | @moduledoc "About the module" 1046 | end 1047 | 1048 | defmodule SomeModule do 1049 | @moduledoc """ 1050 | About the module 1051 | 1052 | Examples: 1053 | iex> SomeModule.some_function 1054 | :result 1055 | """ 1056 | end 1057 | 1058 | # 好 1059 | defmodule SomeModule do 1060 | @moduledoc """ 1061 | About the module 1062 | 1063 | ## Examples 1064 | 1065 | iex> SomeModule.some_function 1066 | :result 1067 | """ 1068 | end 1069 | ``` 1070 | 1071 | ### 类型声明 (Typespecs) 1072 | 1073 | Typespecs是用于声明类型和规格的符号,主要用于文档或是静态分析工具,例如 Dialyzer。 1074 | 1075 | 自定义类型应当与其他指令一起位于模块的顶部。 (详见 [模块](#modules)). 1076 | 1077 | * 1078 | 1079 | 同时使用 `@typedoc` 和 `@type`,并且每对之间使用空行间隔。 1080 | 1081 | [[link](#typedocs)] 1082 | 1083 | ```elixir 1084 | defmodule SomeModule do 1085 | @moduledoc false 1086 | 1087 | @typedoc "The name" 1088 | @type name :: atom 1089 | 1090 | @typedoc "The result" 1091 | @type result :: {:ok, term} | {:error, term} 1092 | 1093 | ... 1094 | end 1095 | ``` 1096 | 1097 | * 1098 | 1099 | 如果联合类型 (union type) 声明超过一行,增加新的一行并且使用空格缩进,保持类型对齐。 1100 | 1101 | [[link](#union-types)] 1102 | 1103 | ```elixir 1104 | # 不好 - 没有缩进 1105 | @type long_union_type :: some_type | another_type | some_other_type | 1106 | a_final_type 1107 | 1108 | # 好 1109 | @type long_union_type :: some_type | another_type | some_other_type | 1110 | a_final_type 1111 | 1112 | # 同样好 - 每一个类型单独一行 1113 | @type long_union_type :: some_type | 1114 | another_type | 1115 | some_other_type | 1116 | a_final_type 1117 | ``` 1118 | 1119 | * 1120 | 1121 | 模块的主类型命名为 `t`,例子:结构的类型声明。 1122 | 1123 | [[link](#naming-main-types)] 1124 | 1125 | ```elixir 1126 | defstruct name: nil, params: [] 1127 | 1128 | @type t :: %__MODULE__{ 1129 | name: String.t | nil, 1130 | params: Keyword.t 1131 | } 1132 | ``` 1133 | 1134 | * 1135 | 1136 | 将函数的类型声明放到函数定义之上,不用空行间隔。 1137 | 1138 | [[link](#spec-spacing)] 1139 | 1140 | ```elixir 1141 | @spec some_function(term) :: result 1142 | def some_function(some_data) do 1143 | {:ok, some_data} 1144 | end 1145 | ``` 1146 | 1147 | ### 结构 1148 | 1149 | * 1150 | 默认值为 `nil` 的结构字段使用原子符号列表 (list of atoms),后面紧跟着其他关键字。 1151 | 1152 | [[link](#nil-struct-field-defaults)] 1153 | 1154 | ```elixir 1155 | # 不好 1156 | defstruct name: nil, params: nil, active: true 1157 | 1158 | # 好 1159 | defstruct [:name, :params, active: true] 1160 | ``` 1161 | 1162 | * 1163 | 1164 | 如果 `defstruct` 的参数是关键字列表 (keyword list),则省略括号。 1165 | 1166 | [[link](#struct-def-brackets)] 1167 | 1168 | ```elixir 1169 | # 不好 1170 | defstruct [params: [], active: true] 1171 | 1172 | # 好 1173 | defstruct params: [], active: true 1174 | 1175 | # 必须 - 由于至少有一个原子符号,括号不可以省略 1176 | defstruct [:name, params: [], active: true] 1177 | ``` 1178 | 1179 | * 1180 | 1181 | 如果结构定义是多行,保持每行第一个键缩进对齐。 1182 | 1183 | [[link](#additional-struct-def-lines)] 1184 | 1185 | ```elixir 1186 | defstruct foo: "test", bar: true, baz: false, 1187 | qux: false, quux: 1 1188 | ``` 1189 | 1190 | ### 异常 1191 | 1192 | * 1193 | 1194 | 异常的命名以 `Error` 结尾。 1195 | 1196 | [[link](#exception-names)] 1197 | 1198 | ```elixir 1199 | # 不好 1200 | defmodule BadHTTPCode do 1201 | defexception [:message] 1202 | end 1203 | 1204 | defmodule BadHTTPCodeException do 1205 | defexception [:message] 1206 | end 1207 | 1208 | # 好 1209 | defmodule BadHTTPCodeError do 1210 | defexception [:message] 1211 | end 1212 | ``` 1213 | 1214 | * 1215 | 1216 | 抛出异常时,异常信息使用小写,并且最后不需要添加标点符号。 1217 | 1218 | [[link](#lowercase-error-messages)] 1219 | 1220 | ```elixir 1221 | # 不好 1222 | raise ArgumentError, "This is not valid." 1223 | 1224 | # 好 1225 | raise ArgumentError, "this is not valid" 1226 | ``` 1227 | 1228 | ### 集合 1229 | 1230 | 暂无内容。 1231 | 1232 | ### 字符串 1233 | 1234 | * 1235 | 1236 | 字符串进行模式匹配时,使用字符串拼接的方式而不要使用二进制的方式。 1237 | 1238 | [[link](#strings-matching-with-concatenator)] 1239 | 1240 | ```elixir 1241 | # 不好 1242 | <<"my"::utf8, _rest>> = "my string" 1243 | 1244 | # 好 1245 | "my" <> _rest = "my string" 1246 | ``` 1247 | 1248 | ### 正则表达式 1249 | 1250 | 暂无内容。 1251 | 1252 | ### 元编程 (metaprogramming) 1253 | 1254 | * 1255 | 避免不必要的元编程。 1256 | [[link](#avoid-metaprogramming)] 1257 | 1258 | ### 测试 1259 | 1260 | * 1261 | 1262 | 在编写 [ExUnit] 断言 (assertions) 时,保持预期值和测试值顺序的一致性。 1263 | 1264 | 尽量把预期值放在右边,除非这条断言在进行模式匹配。 1265 | 1266 | [[link](#testing-assert-order)] 1267 | 1268 | ```elixir 1269 | # 好 - 预期值在右边 1270 | assert actual_function(1) == true 1271 | assert actual_function(2) == false 1272 | 1273 | # 不好 - 顺序不一致 1274 | assert actual_function(1) == true 1275 | assert false == actual_function(2) 1276 | 1277 | # 必要 - 断言是模式匹配 1278 | assert {:ok, expected} = actual_function(3) 1279 | ``` 1280 | 1281 | ### 更多风格指南 1282 | 1283 | * [Aleksei Magusev's Elixir Style Guide](https://github.com/lexmag/elixir-style-guide#readme) 1284 | — An opinionated Elixir style guide stemming from the coding style practiced 1285 | in the Elixir core libraries. 1286 | Developed by [Aleksei Magusev](https://github.com/lexmag) and 1287 | [Andrea Leopardi](https://github.com/whatyouhide), members of Elixir core team. 1288 | While the Elixir project doesn't adhere to any specific style guide, 1289 | this is the closest available guide to its conventions. 1290 | 1291 | * [Credo's Elixir Style Guide](https://github.com/rrrene/elixir-style-guide#readme) 1292 | — Style Guide for the Elixir language, implemented by 1293 | [Credo](http://credo-ci.org) static code analysis tool. 1294 | 1295 | ### 工具 1296 | 1297 | 参考 [Awesome Elixir][Code Analysis] 来了解可以帮助代码分析的库和工具。 1298 | 1299 | ## 参与文档 1300 | 1301 | ### 贡献 1302 | 1303 | 我们希望这将成为社区讨论 Elixir 最佳实践的中心。 1304 | 1305 | 欢迎发起讨论或提交一个带有改进性质的更新请求。在此提前感谢你的帮助! 1306 | 1307 | 参考 [contributing guidelines][Contributing] 1308 | 和 [code of conduct][Code Of Conduct] 获得更多信息。 1309 | 1310 | ### 口耳相传 1311 | 1312 | 一份社区驱动的风格指南,如果没多少人知道, 对一个社区来说就没有多少用处。 1313 | 1314 | 请转发,关注这份指南,让每一个 Elixir 程序员都知晓这份指南,让每一个 Elixir 程序员都可以贡献这份指南! 1315 | 1316 | ## 授权 1317 | 1318 | ### 协议 1319 | 1320 | ![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) 1321 | 本指南基于 1322 | [Creative Commons Attribution 3.0 Unported License][License] 授权许可。 1323 | 1324 | 1325 | [Chinese Simplified]: https://github.com/geekerzp/elixir_style_guide/blob/master/README-zhCN.md 1326 | [Chinese Traditional]: https://github.com/elixirtw/elixir_style_guide/blob/master/README_zhTW.md 1327 | [Code Analysis]: https://github.com/h4cc/awesome-elixir#code-analysis 1328 | [Code Of Conduct]: https://github.com/christopheradams/elixir_style_guide/blob/master/CODE_OF_CONDUCT.md 1329 | [Conflicting Aliases]: https://elixirforum.com/t/using-aliases-for-fubar-fubar-named-module/1723 1330 | [Contributing]: https://github.com/elixir-lang/elixir/blob/master/CODE_OF_CONDUCT.md 1331 | [Contributors]: https://github.com/christopheradams/elixir_style_guide/graphs/contributors 1332 | [Elixir Style Guide]: https://github.com/christopheradams/elixir_style_guide 1333 | [Elixir]: http://elixir-lang.org 1334 | [ExDoc]: https://github.com/elixir-lang/ex_doc 1335 | [ExUnit]: https://hexdocs.pm/ex_unit/ExUnit.html 1336 | [Guard Expressions]: http://elixir-lang.org/getting-started/case-cond-and-if.html#expressions-in-guard-clauses 1337 | [Hex]: https://hex.pm/packages 1338 | [Japanese]: https://github.com/kenichirow/elixir_style_guide/blob/master/README-jaJP.md 1339 | [Korean]: https://github.com/marocchino/elixir_style_guide/blob/new-korean/README-koKR.md 1340 | [License]: http://creativecommons.org/licenses/by/3.0/deed.en_US 1341 | [Module Attributes]: http://elixir-lang.org/getting-started/module-attributes.html#as-annotations 1342 | [Portuguese]: https://github.com/gusaiani/elixir_style_guide/blob/master/README_ptBR.md 1343 | [Ruby community style guide]: https://github.com/bbatsov/ruby-style-guide 1344 | [Sentence Spacing]: http://en.wikipedia.org/wiki/Sentence_spacing 1345 | [Spanish]: https://github.com/albertoalmagro/elixir_style_guide/blob/spanish/README_esES.md 1346 | [Stargazers]: https://github.com/christopheradams/elixir_style_guide/stargazers 1347 | -------------------------------------------------------------------------------- /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 | * [Alternative Style Guides](#alternative-style-guides) 23 | * [Tools](#tools) 24 | * __[Getting Involved](#getting-involved)__ 25 | * [Contributing](#contributing) 26 | * [Spread the Word](#spread-the-word) 27 | * __[Copying](#copying)__ 28 | * [License](#license) 29 | * [Attribution](#attribution) 30 | 31 | ## Prelude 32 | 33 | > Liquid architecture. It's like jazz — you improvise, you work together, you 34 | > play off each other, you make something, they make something. 35 | > 36 | > —Frank Gehry 37 | 38 | Style matters. 39 | [Elixir] has plenty of style but like all languages it can be stifled. 40 | Don't stifle the style. 41 | 42 | ## The Guide 43 | 44 | This is community style guide for the [Elixir programming language][Elixir]. 45 | Please feel free to make pull requests and suggestions, and be a part of 46 | Elixir's vibrant community. 47 | 48 | If you're looking for other projects to contribute to please see the 49 | [Hex package manager site][Hex]. 50 | 51 | 52 | Translations of the guide are available in the following languages: 53 | 54 | * [Chinese Simplified] 55 | * [Chinese Traditional] 56 | * [Japanese] 57 | * [Korean] 58 | * [Portuguese] 59 | * [Spanish] 60 | 61 | ### Source Code Layout 62 | 63 | * 64 | Use two **spaces** per indentation level. 65 | No hard tabs. 66 | [[link](#spaces-indentation)] 67 | 68 | ```elixir 69 | # not preferred - four spaces 70 | def some_function do 71 | do_something 72 | end 73 | 74 | # preferred 75 | def some_function do 76 | do_something 77 | end 78 | ``` 79 | 80 | * 81 | Use Unix-style line endings (\*BSD/Solaris/Linux/OSX users are covered by 82 | default, Windows users have to be extra careful). 83 | [[link](#line-endings)] 84 | 85 | * 86 | If you're using Git you might want to add the following configuration 87 | setting to protect your project from Windows line endings creeping in: 88 | [[link](#autocrlf)] 89 | 90 | ```sh 91 | git config --global core.autocrlf true 92 | ``` 93 | 94 | * 95 | Use spaces around operators, after commas, colons and semicolons. 96 | Do not put spaces around matched pairs like brackets, parentheses, etc. 97 | Whitespace might be (mostly) irrelevant to the Elixir runtime, but its proper 98 | use is the key to writing easily readable code. 99 | [[link](#spaces)] 100 | 101 | ```elixir 102 | sum = 1 + 2 103 | {a, b} = {2, 3} 104 | [first | rest] = [1, 2, 3] 105 | Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts num end) 106 | ``` 107 | 108 | * 109 | Do not use spaces after non-word operators that only take one argument; or 110 | around the range operator. 111 | [[link](#no-spaces)] 112 | 113 | ```elixir 114 | 0 - 1 == -1 115 | ^pinned = some_func() 116 | 5 in 1..10 117 | ``` 118 | 119 | * 120 | Use blank lines between `def`s to break up a function into logical 121 | paragraphs. 122 | [[link](#def-spacing)] 123 | 124 | ```elixir 125 | def some_function(some_data) do 126 | altered_data = Module.function(data) 127 | end 128 | 129 | def some_function do 130 | result 131 | end 132 | 133 | def some_other_function do 134 | another_result 135 | end 136 | 137 | def a_longer_function do 138 | one 139 | two 140 | 141 | three 142 | four 143 | end 144 | ``` 145 | 146 | * 147 | ...but run single-line `def`s that match for the same function together. 148 | [[link](#single-line-defs)] 149 | 150 | ```elixir 151 | def some_function(nil), do: {:err, "No Value"} 152 | def some_function([]), do: :ok 153 | def some_function([first | rest]) do 154 | some_function(rest) 155 | end 156 | ``` 157 | 158 | * 159 | If you use the `do:` syntax with functions and the line that makes up the 160 | function body is long, put the `do:` on a new line indented one level more 161 | than the previous line. 162 | [[link](#long-dos)] 163 | 164 | ```elixir 165 | def some_function(args), 166 | do: Enum.map(args, fn(arg) -> arg <> " is on a very long line!" end) 167 | ``` 168 | 169 | When you use the convention above and you have more than one function clause 170 | using the `do:` syntax, put the `do:` on a new line for each function clause: 171 | 172 | ```elixir 173 | # not preferred 174 | def some_function([]), do: :empty 175 | def some_function(_), 176 | do: :very_long_line_here 177 | 178 | # preferred 179 | def some_function([]), 180 | do: :empty 181 | def some_function(_), 182 | do: :very_long_line_here 183 | ``` 184 | 185 | * 186 | If you have more than one multi-line `def`s do not use single-line `def`s. 187 | [[link](#multiple-function-defs)] 188 | 189 | ```elixir 190 | def some_function(nil) do 191 | {:err, "No Value"} 192 | end 193 | 194 | def some_function([]) do 195 | :ok 196 | end 197 | 198 | def some_function([first | rest]) do 199 | some_function(rest) 200 | end 201 | 202 | def some_function([first | rest], opts) do 203 | some_function(rest, opts) 204 | end 205 | ``` 206 | 207 | * 208 | Use parentheses for one-arity functions when using the pipe operator (`|>`). 209 | [[link](#parentheses-pipe-operator)] 210 | 211 | ```elixir 212 | # not preferred 213 | some_string |> String.downcase |> String.strip 214 | 215 | # preferred 216 | some_string |> String.downcase() |> String.strip() 217 | ``` 218 | 219 | * 220 | Use the pipe operator to chain functions together. 221 | [[link](#pipe-operator)] 222 | 223 | ```elixir 224 | # not preferred 225 | String.strip(String.downcase(some_string)) 226 | 227 | # preferred 228 | some_string |> String.downcase() |> String.strip() 229 | 230 | # Multiline pipelines are not further indented 231 | some_string 232 | |> String.downcase() 233 | |> String.strip() 234 | 235 | # Multiline pipelines on the right side of a pattern match 236 | # should be indented on a new line 237 | sanitized_string = 238 | some_string 239 | |> String.downcase() 240 | |> String.strip() 241 | ``` 242 | 243 | While this is the preferred method, take into account that copy-pasting 244 | multiline pipelines into IEx might result in a syntax error, as IEx will 245 | evaluate the first line without realizing that the next line has a pipeline. 246 | 247 | * 248 | Avoid using the pipe operator just once. 249 | [[link](#avoid-single-pipelines)] 250 | 251 | ```elixir 252 | # not preferred 253 | some_string |> String.downcase() 254 | 255 | # preferred 256 | String.downcase(some_string) 257 | ``` 258 | 259 | * 260 | Use _bare_ variables in the first part of a function chain. 261 | [[link](#bare-variables)] 262 | 263 | ```elixir 264 | # THE WORST! 265 | # This actually parses as String.strip("nope" |> String.downcase()). 266 | String.strip "nope" |> String.downcase() 267 | 268 | # not preferred 269 | String.strip(some_string) |> String.downcase() |> String.codepoints() 270 | 271 | # preferred 272 | some_string |> String.strip() |> String.downcase() |> String.codepoints() 273 | ``` 274 | 275 | * 276 | When assigning a list that spans multiple lines, start the list on a newline, 277 | and indent the elements to keep them aligned. 278 | [[link](#multiline-list-assign)] 279 | 280 | ```elixir 281 | # not preferred - no indentation 282 | list = [:first_item, :second_item, :next_item, 283 | :last_item] 284 | 285 | # better, but not preferred - with indentation 286 | list = [:first_item, :second_item, :next_item, 287 | :last_item] 288 | 289 | # preferred - list starts on its own line 290 | # good for shorter, more compact lists 291 | list = 292 | [:first_item, :second_item, :next_item, 293 | :last_item] 294 | 295 | # also preferred - when each element is on its own line 296 | # good for long lists, lists with long elements, or lists with comments 297 | list = [ 298 | :first_item, 299 | :second_item, 300 | :next_item, 301 | # comment 302 | :many_items, 303 | :last_item 304 | ] 305 | ``` 306 | 307 | * 308 | Avoid trailing whitespace. 309 | [[link](#trailing-whitespace)] 310 | 311 | * 312 | End each file with a newline. 313 | [[link](#newline-eof)] 314 | 315 | ### Syntax 316 | 317 | * 318 | Use parentheses when a `def` has arguments, and omit them when it doesn't. 319 | [[link](#parentheses)] 320 | 321 | ```elixir 322 | # not preferred 323 | def some_function arg1, arg2 do 324 | # body omitted 325 | end 326 | 327 | def some_function() do 328 | # body omitted 329 | end 330 | 331 | # preferred 332 | def some_function(arg1, arg2) do 333 | # body omitted 334 | end 335 | 336 | def some_function do 337 | # body omitted 338 | end 339 | ``` 340 | 341 | * 342 | Add a blank line after a multiline assignment as a 343 | visual cue that the assignment is 'over'. 344 | [[link](#add-blank-line-after-multiline-assignment)] 345 | 346 | ```elixir 347 | # not preferred 348 | some_string = 349 | "Hello" 350 | |> String.downcase() 351 | |> String.strip() 352 | another_string <> some_string 353 | 354 | # preferred 355 | some_string = 356 | "Hello" 357 | |> String.downcase() 358 | |> String.strip() 359 | 360 | another_string <> some_string 361 | ``` 362 | 363 | ```elixir 364 | # also not preferred 365 | something = 366 | if x == 2 do 367 | "Hi" 368 | else 369 | "Bye" 370 | end 371 | String.downcase(something) 372 | 373 | # preferred 374 | something = 375 | if x == 2 do 376 | "Hi" 377 | else 378 | "Bye" 379 | end 380 | 381 | String.downcase(something) 382 | ``` 383 | 384 | * 385 | Never use `do:` for multi-line `if/unless`. 386 | [[link](#do-with-multi-line-if-unless)] 387 | 388 | ```elixir 389 | # not preferred 390 | if some_condition, do: 391 | # a line of code 392 | # another line of code 393 | # note no end in this block 394 | 395 | # preferred 396 | if some_condition do 397 | # some 398 | # lines 399 | # of code 400 | end 401 | ``` 402 | 403 | * 404 | Use `do:` for single line `if/unless` statements. 405 | [[link](#do-with-single-line-if-unless)] 406 | 407 | ```elixir 408 | # preferred 409 | if some_condition, do: # some_stuff 410 | ``` 411 | 412 | * 413 | Never use `unless` with `else`. 414 | Rewrite these with the positive case first. 415 | [[link](#unless-with-else)] 416 | 417 | ```elixir 418 | # not preferred 419 | unless success? do 420 | IO.puts 'failure' 421 | else 422 | IO.puts 'success' 423 | end 424 | 425 | # preferred 426 | if success? do 427 | IO.puts 'success' 428 | else 429 | IO.puts 'failure' 430 | end 431 | ``` 432 | 433 | * 434 | Use `true` as the last condition of the `cond` special form when you need a 435 | clause that always matches. 436 | [[link](#true-as-last-condition)] 437 | 438 | ```elixir 439 | # not preferred 440 | cond do 441 | 1 + 2 == 5 -> 442 | "Nope" 443 | 1 + 3 == 5 -> 444 | "Uh, uh" 445 | :else -> 446 | "OK" 447 | end 448 | 449 | # preferred 450 | cond do 451 | 1 + 2 == 5 -> 452 | "Nope" 453 | 1 + 3 == 5 -> 454 | "Uh, uh" 455 | true -> 456 | "OK" 457 | end 458 | ``` 459 | 460 | * 461 | Never put a space between a function name and the opening parenthesis. 462 | [[link](#function-names-with-parentheses)] 463 | 464 | ```elixir 465 | # not preferred 466 | f (3 + 2) + 1 467 | 468 | # preferred 469 | f(3 + 2) + 1 470 | ``` 471 | 472 | * 473 | Use parentheses in function calls, especially inside a pipeline. 474 | [[link](#function-calls-and-parentheses)] 475 | 476 | ```elixir 477 | # not preferred 478 | f 3 479 | 480 | # preferred 481 | f(3) 482 | 483 | # not preferred and parses as rem(2, (3 |> g)), which is not what you want. 484 | 2 |> rem 3 |> g 485 | 486 | # preferred 487 | 2 |> rem(3) |> g 488 | ``` 489 | 490 | * 491 | Omit parentheses in macro calls when a do block is passed. 492 | [[link](#macro-calls-and-parentheses)] 493 | 494 | ```elixir 495 | # not preferred 496 | quote(do 497 | foo 498 | end) 499 | 500 | # preferred 501 | quote do 502 | foo 503 | end 504 | ``` 505 | 506 | * 507 | Optionally omit parentheses in function calls (outside a pipeline) when the 508 | last argument is a function expression. 509 | [[link](#parentheses-and-function-expressions)] 510 | 511 | ```elixir 512 | # preferred 513 | Enum.reduce(1..10, 0, fn x, acc -> 514 | x + acc 515 | end) 516 | 517 | # also preferred 518 | Enum.reduce 1..10, 0, fn x, acc -> 519 | x + acc 520 | end 521 | ``` 522 | 523 | * 524 | Use parentheses for calls to functions with zero arity, so they can be 525 | distinguished from variables. 526 | Starting in Elixir 1.4, the compiler will warn you about 527 | locations where this ambiguity exists. 528 | [[link](#parentheses-and-functions-with-zero-arity)] 529 | 530 | ```elixir 531 | defp do_stuff, do: ... 532 | 533 | # not preferred 534 | def my_func do 535 | do_stuff # is this a variable or a function call? 536 | end 537 | 538 | # preferred 539 | def my_func do 540 | do_stuff() # this is clearly a function call 541 | end 542 | ``` 543 | 544 | * 545 | Always use the special syntax for keyword lists. 546 | [[link](#keyword-list-syntax)] 547 | 548 | ```elixir 549 | # not preferred 550 | some_value = [{:a, "baz"}, {:b, "qux"}] 551 | 552 | # preferred 553 | some_value = [a: "baz", b: "qux"] 554 | ``` 555 | 556 | * 557 | Omit square brackets from keyword lists whenever they are optional. 558 | [[link](#keyword-list-brackets)] 559 | 560 | ```elixir 561 | # not preferred 562 | some_function(foo, bar, [a: "baz", b: "qux"]) 563 | 564 | # preferred 565 | some_function(foo, bar, a: "baz", b: "qux") 566 | ``` 567 | 568 | * 569 | Indent and align successive `with` clauses. 570 | Put the `do:` argument on a new line, indented normally. 571 | [[link](#with-clauses)] 572 | 573 | ```elixir 574 | with {:ok, foo} <- fetch(opts, :foo), 575 | {:ok, bar} <- fetch(opts, :bar), 576 | do: {:ok, foo, bar} 577 | ``` 578 | 579 | * 580 | If the `with` expression has a `do` block with more than one line, or has an 581 | `else` option, use multiline syntax. 582 | [[link](#with-else)] 583 | 584 | ```elixir 585 | with {:ok, foo} <- fetch(opts, :foo), 586 | {:ok, bar} <- fetch(opts, :bar) do 587 | {:ok, foo, bar} 588 | else 589 | :error -> 590 | {:error, :bad_arg} 591 | end 592 | ``` 593 | 594 | ### Naming 595 | 596 | * 597 | Use `snake_case` for atoms, functions and variables. 598 | [[link](#snake-case)] 599 | 600 | ```elixir 601 | # not preferred 602 | :"some atom" 603 | :SomeAtom 604 | :someAtom 605 | 606 | someVar = 5 607 | 608 | def someFunction do 609 | ... 610 | end 611 | 612 | def SomeFunction do 613 | ... 614 | end 615 | 616 | # preferred 617 | :some_atom 618 | 619 | some_var = 5 620 | 621 | def some_function do 622 | ... 623 | end 624 | ``` 625 | 626 | * 627 | Use `CamelCase` for modules (keep acronyms like HTTP, RFC, XML uppercase). 628 | [[link](#camel-case)] 629 | 630 | ```elixir 631 | # not preferred 632 | defmodule Somemodule do 633 | ... 634 | end 635 | 636 | defmodule Some_Module do 637 | ... 638 | end 639 | 640 | defmodule SomeXml do 641 | ... 642 | end 643 | 644 | # preferred 645 | defmodule SomeModule do 646 | ... 647 | end 648 | 649 | defmodule SomeXML do 650 | ... 651 | end 652 | ``` 653 | 654 | * 655 | The names of predicate macros (compile-time generated functions that return a 656 | boolean value) _that can be used within guards_ should be prefixed with `is_`. 657 | For a list of allowed expressions, see the [Guard][Guard Expressions] docs. 658 | [[link](#predicate-macro-names-with-guards)] 659 | 660 | ```elixir 661 | defmacro is_cool(var) do 662 | quote do: unquote(var) == "cool" 663 | end 664 | ``` 665 | 666 | * 667 | The names of predicate functions _that cannot be used within guards_ should 668 | have a trailing question mark (`?`) rather than the `is_` (or similar) prefix. 669 | [[link](#predicate-macro-names-no-guards)] 670 | 671 | ```elixir 672 | def cool?(var) do 673 | # Complex check if var is cool not possible in a pure function. 674 | end 675 | ``` 676 | 677 | * 678 | Private functions with the same name as public functions should start with 679 | `do_`. 680 | [[link](#private-functions-with-same-name-as-public)] 681 | 682 | ```elixir 683 | def sum(list), do: do_sum(list, 0) 684 | 685 | # private functions 686 | defp do_sum([], total), do: total 687 | defp do_sum([head | tail], total), do: do_sum(tail, head + total) 688 | ``` 689 | 690 | ### Comments 691 | 692 | * 693 | Write expressive code and try to convey your program's intention through 694 | control-flow, structure and naming. 695 | [[link](#expressive-code)] 696 | 697 | * 698 | Use one space between the leading `#` character of the comment and the text of 699 | the comment. 700 | [[link](#comment-leading-spaces)] 701 | 702 | ```elixir 703 | String.first(some_string) #not preferred 704 | String.first(some_string) # preferred 705 | ``` 706 | 707 | * 708 | Comments longer than a word are capitalized, and sentences use punctuation. 709 | Use [one space][Sentence Spacing] after periods. 710 | [[link](#comment-grammar)] 711 | 712 | ```elixir 713 | # not preferred 714 | # these lowercase comments are missing punctuation 715 | 716 | # preferred 717 | # Capitalization example 718 | # Use punctuation for complete sentences. 719 | ``` 720 | 721 | #### Comment Annotations 722 | 723 | * 724 | Annotations should usually be written on the line immediately above the 725 | relevant code. 726 | [[link](#annotations)] 727 | 728 | * 729 | The annotation keyword is uppercase, and is followed by a colon and a space, 730 | then a note describing the problem. 731 | [[link](#annotation-keyword)] 732 | 733 | ```elixir 734 | # TODO: Deprecate in v1.5. 735 | def some_function(arg), do: {:ok, arg} 736 | ``` 737 | 738 | * 739 | In cases where the problem is so obvious that any documentation would be 740 | redundant, annotations may be left at the end of the offending line with no 741 | note. 742 | This usage should be the exception and not the rule. 743 | [[link](#exceptions-to-annotations)] 744 | 745 | ```elixir 746 | start_task() 747 | Process.sleep(5000) # FIXME 748 | ``` 749 | 750 | * 751 | Use `TODO` to note missing features or functionality that should be added at a 752 | later date. 753 | [[link](#todo-notes)] 754 | 755 | * 756 | Use `FIXME` to note broken code that needs to be fixed. 757 | [[link](#fixme-notes)] 758 | 759 | * 760 | Use `OPTIMIZE` to note slow or inefficient code that may cause performance 761 | problems. 762 | [[link](#optimize-notes)] 763 | 764 | * 765 | Use `HACK` to note code smells where questionable coding practices were used 766 | and should be refactored away. 767 | [[link](#hack-notes)] 768 | 769 | * 770 | Use `REVIEW` to note anything that should be looked at to confirm it is 771 | working as intended. 772 | For example: `REVIEW: Are we sure this is how the client does X currently?` 773 | [[link](#review-notes)] 774 | 775 | * 776 | Use other custom annotation keywords if it feels appropriate, but be sure to 777 | document them in your project's `README` or similar. 778 | [[link](#custom-keywords)] 779 | 780 | ### Modules 781 | 782 | * 783 | Use one module per file unless the module is only used internally by another 784 | module (such as a test). 785 | [[link](#one-module-per-file)] 786 | 787 | * 788 | Use `snake_case` file names for `CamelCase` module names. 789 | [[link](#underscored-filenames)] 790 | 791 | ```elixir 792 | # file is called some_module.ex 793 | 794 | defmodule SomeModule do 795 | end 796 | ``` 797 | 798 | * 799 | Represent each level of nesting within a module name as a directory. 800 | [[link](#module-name-nesting)] 801 | 802 | ```elixir 803 | # file is called parser/core/xml_parser.ex 804 | 805 | defmodule Parser.Core.XMLParser do 806 | end 807 | ``` 808 | 809 | * 810 | Don't put a blank line after `defmodule`. 811 | [[link](#defmodule-spacing)] 812 | 813 | * 814 | Put a blank line after module-level code blocks. 815 | [[link](#module-block-spacing)] 816 | 817 | * 818 | List module attributes and directives in the following order: 819 | [[link](#module-attribute-ordering)] 820 | 821 | 1. `@moduledoc` 822 | 1. `@behaviour` 823 | 1. `use` 824 | 1. `import` 825 | 1. `alias` 826 | 1. `require` 827 | 1. `defstruct` 828 | 1. `@type` 829 | 1. `@module_attribute` 830 | 1. `@callback` 831 | 1. `@macrocallback` 832 | 1. `@optional_callbacks` 833 | 834 | Add a blank line between each grouping, and sort the terms (like module names) 835 | alphabetically. 836 | Here's an overall example of how you should order things in your modules: 837 | 838 | ```elixir 839 | defmodule MyModule do 840 | @moduledoc """ 841 | An example module 842 | """ 843 | 844 | @behaviour MyBehaviour 845 | 846 | use GenServer 847 | 848 | import Something 849 | import SomethingElse 850 | 851 | alias My.Long.Module.Name 852 | alias My.Other.Module.Example 853 | 854 | require Integer 855 | 856 | defstruct name: nil, params: [] 857 | 858 | @type params :: [{binary, binary}] 859 | 860 | @module_attribute :foo 861 | @other_attribute 100 862 | 863 | @callback some_function(term) :: :ok | {:error, term} 864 | 865 | @macrocallback macro_name(term) :: Macro.t 866 | 867 | @optional_callbacks macro_name: 1 868 | 869 | ... 870 | end 871 | ``` 872 | 873 | * 874 | Use the `__MODULE__` pseudo variable when a module refers to itself. This 875 | avoids having to update any self-references when the module name changes. 876 | [[link](#module-pseudo-variable)] 877 | 878 | ```elixir 879 | defmodule SomeProject.SomeModule do 880 | defstruct [:name] 881 | 882 | def name(%__MODULE__{name: name}), do: name 883 | end 884 | ``` 885 | 886 | * 887 | If you want a prettier name for a module self-reference, set up an alias. 888 | [[link](#alias-self-referencing-modules)] 889 | 890 | ```elixir 891 | defmodule SomeProject.SomeModule do 892 | alias __MODULE__, as: SomeModule 893 | 894 | defstruct [:name] 895 | 896 | def name(%SomeModule{name: name}), do: name 897 | end 898 | ``` 899 | 900 | * 901 | Avoid repeating fragments in module names and namespaces. 902 | This improves overall readability and 903 | eliminates [ambiguous aliases][Conflicting Aliases]. 904 | [[link](#repetitive-module-names)] 905 | 906 | ```elixir 907 | # not preferred 908 | defmodule Todo.Todo do 909 | ... 910 | end 911 | 912 | # preferred 913 | defmodule Todo.Item do 914 | ... 915 | end 916 | ``` 917 | 918 | ### Documentation 919 | 920 | Documentation in Elixir (when read either in `iex` with `h` or generated with 921 | [ExDoc]) uses the [Module Attributes] `@moduledoc` and `@doc`. 922 | 923 | * 924 | Always include a `@moduledoc` attribute in the line right after `defmodule` in 925 | your module. 926 | [[link](#moduledocs)] 927 | 928 | ```elixir 929 | # not preferred 930 | 931 | defmodule SomeModule do 932 | 933 | @moduledoc """ 934 | About the module 935 | """ 936 | ... 937 | end 938 | 939 | defmodule AnotherModule do 940 | use SomeModule 941 | @moduledoc """ 942 | About the module 943 | """ 944 | ... 945 | end 946 | 947 | # preferred 948 | 949 | defmodule SomeModule do 950 | @moduledoc """ 951 | About the module 952 | """ 953 | ... 954 | end 955 | ``` 956 | 957 | * 958 | Use `@moduledoc false` if you do not intend on documenting the module. 959 | [[link](#moduledoc-false)] 960 | 961 | ```elixir 962 | defmodule SomeModule do 963 | @moduledoc false 964 | ... 965 | end 966 | ``` 967 | 968 | * 969 | Separate code after the `@moduledoc` with a blank line. 970 | [[link](#moduledoc-spacing)] 971 | 972 | ```elixir 973 | # not preferred 974 | 975 | defmodule SomeModule do 976 | @moduledoc """ 977 | About the module 978 | """ 979 | use AnotherModule 980 | end 981 | 982 | # preferred 983 | defmodule SomeModule do 984 | @moduledoc """ 985 | About the module 986 | """ 987 | 988 | use AnotherModule 989 | end 990 | ``` 991 | 992 | * 993 | Use heredocs with markdown for documentation. 994 | [[link](#heredocs)] 995 | 996 | ```elixir 997 | # not preferred 998 | 999 | defmodule SomeModule do 1000 | @moduledoc "About the module" 1001 | end 1002 | 1003 | defmodule SomeModule do 1004 | @moduledoc """ 1005 | About the module 1006 | 1007 | Examples: 1008 | iex> SomeModule.some_function 1009 | :result 1010 | """ 1011 | end 1012 | 1013 | # preferred 1014 | defmodule SomeModule do 1015 | @moduledoc """ 1016 | About the module 1017 | 1018 | ## Examples 1019 | 1020 | iex> SomeModule.some_function 1021 | :result 1022 | """ 1023 | end 1024 | ``` 1025 | 1026 | ### Typespecs 1027 | 1028 | Typespecs are notation for declaring types and specifications, for 1029 | documentation or for the static analysis tool Dialyzer. 1030 | 1031 | Custom types should be defined at the top of the module with the other 1032 | directives (see [Modules](#modules)). 1033 | 1034 | * 1035 | Place `@typedoc` and `@type` definitions together, and separate each 1036 | pair with a blank line. 1037 | [[link](#typedocs)] 1038 | 1039 | ```elixir 1040 | defmodule SomeModule do 1041 | @moduledoc false 1042 | 1043 | @typedoc "The name" 1044 | @type name :: atom 1045 | 1046 | @typedoc "The result" 1047 | @type result :: {:ok, term} | {:error, term} 1048 | 1049 | ... 1050 | end 1051 | ``` 1052 | 1053 | * 1054 | If a union type is too long to fit on a single line, add a newline 1055 | and indent with spaces to keep the types aligned. 1056 | [[link](#union-types)] 1057 | 1058 | ```elixir 1059 | # not preferred - no indentation 1060 | @type long_union_type :: some_type | another_type | some_other_type | 1061 | a_final_type 1062 | 1063 | # preferred 1064 | @type long_union_type :: some_type | another_type | some_other_type | 1065 | a_final_type 1066 | 1067 | # also preferred - one type per line 1068 | @type long_union_type :: some_type | 1069 | another_type | 1070 | some_other_type | 1071 | a_final_type 1072 | ``` 1073 | 1074 | * 1075 | Name the main type for a module `t`, for example: the type specification for a 1076 | struct. 1077 | [[link](#naming-main-types)] 1078 | 1079 | ```elixir 1080 | defstruct name: nil, params: [] 1081 | 1082 | @type t :: %__MODULE__{ 1083 | name: String.t | nil, 1084 | params: Keyword.t 1085 | } 1086 | ``` 1087 | 1088 | * 1089 | Place specifications right before the function definition, 1090 | without separating them by a blank line. 1091 | [[link](#spec-spacing)] 1092 | 1093 | ```elixir 1094 | @spec some_function(term) :: result 1095 | def some_function(some_data) do 1096 | {:ok, some_data} 1097 | end 1098 | ``` 1099 | 1100 | ### Structs 1101 | 1102 | * 1103 | Use a list of atoms for struct fields that default to `nil`, followed by the 1104 | other keywords. 1105 | [[link](#nil-struct-field-defaults)] 1106 | 1107 | ```elixir 1108 | # not preferred 1109 | defstruct name: nil, params: nil, active: true 1110 | 1111 | # preferred 1112 | defstruct [:name, :params, active: true] 1113 | ``` 1114 | 1115 | * 1116 | Omit square brackets when the argument of a `defstruct` is a keyword list. 1117 | [[link](#struct-def-brackets)] 1118 | 1119 | ```elixir 1120 | # not preferred 1121 | defstruct [params: [], active: true] 1122 | 1123 | # preferred 1124 | defstruct params: [], active: true 1125 | 1126 | # required - brackets are not optional, with at least one atom in the list 1127 | defstruct [:name, params: [], active: true] 1128 | ``` 1129 | 1130 | * 1131 | Indent additional lines of the struct definition, keeping the first keys 1132 | aligned. 1133 | [[link](#additional-struct-def-lines)] 1134 | 1135 | ```elixir 1136 | defstruct foo: "test", bar: true, baz: false, 1137 | qux: false, quux: 1 1138 | ``` 1139 | 1140 | ### Exceptions 1141 | 1142 | * 1143 | Make exception names end with a trailing `Error`. 1144 | [[link](#exception-names)] 1145 | 1146 | ```elixir 1147 | # not preferred 1148 | defmodule BadHTTPCode do 1149 | defexception [:message] 1150 | end 1151 | 1152 | defmodule BadHTTPCodeException do 1153 | defexception [:message] 1154 | end 1155 | 1156 | # preferred 1157 | defmodule BadHTTPCodeError do 1158 | defexception [:message] 1159 | end 1160 | ``` 1161 | 1162 | * 1163 | Use lowercase error messages when raising exceptions, with no trailing 1164 | punctuation. 1165 | [[link](#lowercase-error-messages)] 1166 | 1167 | ```elixir 1168 | # not preferred 1169 | raise ArgumentError, "This is not valid." 1170 | 1171 | # preferred 1172 | raise ArgumentError, "this is not valid" 1173 | ``` 1174 | 1175 | ### Collections 1176 | 1177 | _No guidelines for collections have been added yet._ 1178 | 1179 | ### Strings 1180 | 1181 | * 1182 | Match strings using the string concatenator rather than binary patterns: 1183 | [[link](#strings-matching-with-concatenator)] 1184 | 1185 | ```elixir 1186 | # not preferred 1187 | <<"my"::utf8, _rest>> = "my string" 1188 | 1189 | # preferred 1190 | "my" <> _rest = "my string" 1191 | ``` 1192 | 1193 | ### Regular Expressions 1194 | 1195 | _No guidelines for regular expressions have been added yet._ 1196 | 1197 | ### Metaprogramming 1198 | 1199 | * 1200 | Avoid needless metaprogramming. 1201 | [[link](#avoid-metaprogramming)] 1202 | 1203 | ### Testing 1204 | 1205 | * 1206 | When writing [ExUnit] assertions, be consistent with the order of the expected 1207 | and actual values under testing. 1208 | Prefer placing the expected result on the right, unless the assertion is a 1209 | pattern match. 1210 | [[link](#testing-assert-order)] 1211 | 1212 | ```elixir 1213 | # preferred - expected result on the right 1214 | assert actual_function(1) == true 1215 | assert actual_function(2) == false 1216 | 1217 | # not preferred - inconsistent order 1218 | assert actual_function(1) == true 1219 | assert false == actual_function(2) 1220 | 1221 | # required - the assertion is a pattern match 1222 | assert {:ok, expected} = actual_function(3) 1223 | ``` 1224 | 1225 | ### Alternative Style Guides 1226 | 1227 | * [Aleksei Magusev's Elixir Style Guide](https://github.com/lexmag/elixir-style-guide#readme) 1228 | — An opinionated Elixir style guide stemming from the coding style practiced 1229 | in the Elixir core libraries. 1230 | Developed by [Aleksei Magusev](https://github.com/lexmag) and 1231 | [Andrea Leopardi](https://github.com/whatyouhide), members of Elixir core team. 1232 | While the Elixir project doesn't adhere to any specific style guide, 1233 | this is the closest available guide to its conventions. 1234 | 1235 | * [Credo's Elixir Style Guide](https://github.com/rrrene/elixir-style-guide#readme) 1236 | — Style Guide for the Elixir language, implemented by 1237 | [Credo](http://credo-ci.org) static code analysis tool. 1238 | 1239 | ### Tools 1240 | 1241 | Refer to [Awesome Elixir][Code Analysis] for libraries and tools that can help 1242 | with code analysis and style linting. 1243 | 1244 | ## Getting Involved 1245 | 1246 | ### Contributing 1247 | 1248 | It's our hope that this will become a central hub for community discussion on 1249 | best practices in Elixir. 1250 | Feel free to open tickets or send pull requests with improvements. 1251 | Thanks in advance for your help! 1252 | 1253 | Check the [contributing guidelines][Contributing] 1254 | and [code of conduct][Code Of Conduct] for more information. 1255 | 1256 | ### Spread the Word 1257 | 1258 | A community style guide is meaningless without the community's support. Please 1259 | tweet, [star][Stargazers], and let any Elixir programmer know 1260 | about [this guide][Elixir Style Guide] so they can contribute. 1261 | 1262 | ## Copying 1263 | 1264 | ### License 1265 | 1266 | ![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) 1267 | This work is licensed under a 1268 | [Creative Commons Attribution 3.0 Unported License][License] 1269 | 1270 | ### Attribution 1271 | 1272 | The structure of this guide, bits of example code, and many of the initial 1273 | points made in this document were borrowed from the [Ruby community style guide]. 1274 | A lot of things were applicable to Elixir and allowed us to get _some_ document 1275 | out quicker to start the conversation. 1276 | 1277 | Here's the [list of people who have kindly contributed][Contributors] to this 1278 | project. 1279 | 1280 | 1281 | [Chinese Simplified]: https://github.com/geekerzp/elixir_style_guide/blob/master/README-zhCN.md 1282 | [Chinese Traditional]: https://github.com/elixirtw/elixir_style_guide/blob/master/README_zhTW.md 1283 | [Code Analysis]: https://github.com/h4cc/awesome-elixir#code-analysis 1284 | [Code Of Conduct]: https://github.com/christopheradams/elixir_style_guide/blob/master/CODE_OF_CONDUCT.md 1285 | [Conflicting Aliases]: https://elixirforum.com/t/using-aliases-for-fubar-fubar-named-module/1723 1286 | [Contributing]: https://github.com/elixir-lang/elixir/blob/master/CODE_OF_CONDUCT.md 1287 | [Contributors]: https://github.com/christopheradams/elixir_style_guide/graphs/contributors 1288 | [Elixir Style Guide]: https://github.com/christopheradams/elixir_style_guide 1289 | [Elixir]: http://elixir-lang.org 1290 | [ExDoc]: https://github.com/elixir-lang/ex_doc 1291 | [ExUnit]: https://hexdocs.pm/ex_unit/ExUnit.html 1292 | [Guard Expressions]: http://elixir-lang.org/getting-started/case-cond-and-if.html#expressions-in-guard-clauses 1293 | [Hex]: https://hex.pm/packages 1294 | [Japanese]: https://github.com/kenichirow/elixir_style_guide/blob/master/README-jaJP.md 1295 | [Korean]: https://github.com/marocchino/elixir_style_guide/blob/new-korean/README-koKR.md 1296 | [License]: http://creativecommons.org/licenses/by/3.0/deed.en_US 1297 | [Module Attributes]: http://elixir-lang.org/getting-started/module-attributes.html#as-annotations 1298 | [Portuguese]: https://github.com/gusaiani/elixir_style_guide/blob/master/README_ptBR.md 1299 | [Ruby community style guide]: https://github.com/bbatsov/ruby-style-guide 1300 | [Sentence Spacing]: http://en.wikipedia.org/wiki/Sentence_spacing 1301 | [Spanish]: https://github.com/albertoalmagro/elixir_style_guide/blob/spanish/README_esES.md 1302 | [Stargazers]: https://github.com/christopheradams/elixir_style_guide/stargazers 1303 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :test do 2 | exit_code = 0 3 | files = ["README.md", "CONTRIBUTING.md"] 4 | # 'MD036' # Emphasis used instead of a header 5 | # 'MD033' # Inline HTML - allow for anchor links in each bullet point 6 | rules = ['~MD036', '~MD033'].join(",") 7 | files.each do |file| 8 | begin 9 | sh "mdl --rules #{rules} #{file}" 10 | rescue Exception => ex 11 | exit_code = 1 12 | end 13 | end 14 | 15 | exit exit_code 16 | end 17 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirStyleGuide.Mixfile do 2 | use Mix.Project 3 | 4 | @project_description """ 5 | A community driven style guide for Elixir 6 | """ 7 | 8 | @version "0.1.0" 9 | @source_url "https://github.com/christopheradams/elixir_style_guide" 10 | 11 | def project do 12 | [app: :elixir_style_guide, 13 | version: @version, 14 | elixir: "~> 1.0", 15 | build_embedded: Mix.env == :prod, 16 | start_permanent: Mix.env == :prod, 17 | docs: docs(), 18 | description: @project_description, 19 | source_url: @source_url, 20 | package: package(), 21 | deps: deps()] 22 | end 23 | 24 | def application do 25 | [applications: [:logger]] 26 | end 27 | 28 | defp deps do 29 | [] 30 | end 31 | 32 | defp docs() do 33 | [ 34 | source_ref: "v#{@version}", 35 | main: "readme", 36 | extras: [ 37 | "README.md": [title: "README"] 38 | ] 39 | ] 40 | end 41 | 42 | defp package do 43 | [ 44 | name: :elixir_style_guide, 45 | maintainers: ["Christopher Adams"], 46 | licenses: ["CC-by"], 47 | links: %{ 48 | "GitHub" => @source_url 49 | } 50 | ] 51 | end 52 | end 53 | --------------------------------------------------------------------------------