Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A community driven style guide for Elixir

NotificationsYou must be signed in to change notification settings

christopheradams/elixir_style_guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents

Prelude

Liquid architecture. It's like jazz — you improvise, you work together, youplay off each other, you make something, they make something.

—Frank Gehry

Style matters.Elixir has plenty of style but like all languages it can be stifled.Don't stifle the style.

About

This is community style guide for theElixir programming language.Please feel free to make pull requests and suggestions, and be a part ofElixir's vibrant community.

If you're looking for other projects to contribute to please see theHex package manager site.

Translations of the guide are available in the following languages:

Formatting

Elixir v1.6 introduced aCode Formatter andMix format task.The formatter should be preferred for all new projects and source code.

The rules in this section are applied automatically by the code formatter, butare provided here as examples of the preferred style.

Whitespace

  • Avoid trailing whitespace.[link]

  • End each file with a newline.[link]

  • Use Unix-style line endings (*BSD/Solaris/Linux/OSX users are covered bydefault, Windows users have to be extra careful).[link]

  • If you're using Git you might want to add the following configurationsetting to protect your project from Windows line endings creeping in:[link]

    git config --global core.autocrlftrue
  • Limit lines to 98 characters.Otherwise, set the:line_length option in your.formatter.exs file.[link]

  • Use spaces around operators, after commas, colons and semicolons.Do not put spaces around matched pairs like brackets, parentheses, etc.Whitespace might be (mostly) irrelevant to the Elixir runtime, but its properuse is the key to writing easily readable code.[link]

    sum=1+2{a,b}={2,3}[first|rest]=[1,2,3]Enum.map(["one",<<"two">>,"three"],fnnum->IO.puts(num)end)
  • Do not use spaces after non-word operators that only take one argument; oraround the range operator.[link]

    0-1==-1^pinned=some_func()5in1..10
  • Use blank lines betweendefs to break up a function into logicalparagraphs.[link]

    defsome_function(some_data)dosome_data|>other_function()|>List.first()enddefsome_functiondoresultenddefsome_other_functiondoanother_resultenddefa_longer_functiondoonetwothreefourend
  • Don't put a blank line afterdefmodule.[link]

  • If the function head anddo: clause are too long to fit on the same line, putdo: on a new line, indented one level more than the previous line.[link]

    defsome_function([:foo,:bar,:baz]=args),do:Enum.map(args,fnarg->arg<>" is on a very long line!"end)

    When thedo: clause starts on its own line, treat it as a multilinefunction by separating it with blank lines.

    # not preferreddefsome_function([]),do::emptydefsome_function(_),do::very_long_line_here# preferreddefsome_function([]),do::emptydefsome_function(_),do::very_long_line_here
  • Add a blank line after a multiline assignment as avisual cue that the assignment is 'over'.[link]

    # not preferredsome_string="Hello"|>String.downcase()|>String.trim()another_string<>some_string# preferredsome_string="Hello"|>String.downcase()|>String.trim()another_string<>some_string
    # also not preferredsomething=ifx==2do"Hi"else"Bye"endString.downcase(something)# preferredsomething=ifx==2do"Hi"else"Bye"endString.downcase(something)
  • If a list, map, or struct spans multiple lines, put each element, as well asthe opening and closing brackets, on its own line.Indent each element one level, but not the brackets.[link]

    # not preferred[:first_item,:second_item,:next_item,:final_item]# preferred[:first_item,:second_item,:next_item,:final_item]
  • When assigning a list, map, or struct, keep the opening bracket on the sameline as the assignment.[link]

    # not preferredlist=[:first_item,:second_item]# preferredlist=[:first_item,:second_item]
  • If anycase orcond clause needs more than one line (due to line length,multiple expressions in the clause body, etc.), use multi-line syntax for allclauses, and separate each one with a blank line.[link]

    # not preferredcaseargdotrue->IO.puts("ok");:okfalse->:errorend# not preferredcaseargdotrue->IO.puts("ok"):okfalse->:errorend# preferredcaseargdotrue->IO.puts("ok"):okfalse->:errorend
  • Place comments above the line they comment on.[link]

    String.first(some_string)# not preferred# preferredString.first(some_string)
  • Use one space between the leading# character of the comment and the text ofthe comment.[link]

    #not preferredString.first(some_string)# preferredString.first(some_string)

Indentation

  • Indent and align successivewith clauses.Put thedo: argument on a new line, aligned with the previous clauses.[link]

    with{:ok,foo}<-fetch(opts,:foo),{:ok,my_var}<-fetch(opts,:my_var),do:{:ok,foo,my_var}
  • If thewith expression has ado block with more than one line, or has anelse option, use multiline syntax.[link]

    with{:ok,foo}<-fetch(opts,:foo),{:ok,my_var}<-fetch(opts,:my_var)do{:ok,foo,my_var}else:error->{:error,:bad_arg}end

Parentheses

  • Use parentheses for one-arity functions when using the pipe operator (|>).[link]

    # not preferredsome_string|>String.downcase|>String.trim# preferredsome_string|>String.downcase()|>String.trim()
  • Never put a space between a function name and the opening parenthesis.[link]

    # not preferredf(3+2)# preferredf(3+2)
  • Use parentheses in function calls, especially inside a pipeline.[link]

    # not preferredf3# preferredf(3)# not preferred and parses as rem(2, (3 |> g)), which is not what you want.2|>rem3|>g# preferred2|>rem(3)|>g()
  • Omit square brackets from keyword lists whenever they are optional.[link]

    # not preferredsome_function(foo,bar,[a:"baz",b:"qux"])# preferredsome_function(foo,bar,a:"baz",b:"qux")

The Guide

The rules in this section may not be applied by the code formatter, but they aregenerally preferred practice.

Expressions

  • Run single-linedefs that match for the same function together, but separatemultilinedefs with a blank line.[link]

    defsome_function(nil),do:{:error,"No Value"}defsome_function([]),do::okdefsome_function([first|rest])dosome_function(rest)end
  • If you have more than one multilinedef, do not use single-linedefs.[link]

    defsome_function(nil)do{:error,"No Value"}enddefsome_function([])do:okenddefsome_function([first|rest])dosome_function(rest)enddefsome_function([first|rest],opts)dosome_function(rest,opts)end
  • Use the pipe operator to chain functions together.[link]

    # not preferredString.trim(String.downcase(some_string))# preferredsome_string|>String.downcase()|>String.trim()# Multiline pipelines are not further indentedsome_string|>String.downcase()|>String.trim()# Multiline pipelines on the right side of a pattern match# should be indented on a new linesanitized_string=some_string|>String.downcase()|>String.trim()

    While this is the preferred method, take into account that copy-pastingmultiline pipelines into IEx might result in a syntax error, as IEx willevaluate the first line without realizing that the next line has a pipeline.To avoid this, you can wrap the pasted code in parentheses.

  • Avoid using the pipe operator just once.[link]

    # not preferredsome_string|>String.downcase()System.version()|>Version.parse()# preferredString.downcase(some_string)Version.parse(System.version())
  • Usebare variables in the first part of a function chain.[link]

    # not preferredString.trim(some_string)|>String.downcase()|>String.codepoints()# preferredsome_string|>String.trim()|>String.downcase()|>String.codepoints()
  • Use parentheses when adef has arguments, and omit them when it doesn't.[link]

    # not preferreddefsome_functionarg1,arg2do# body omittedenddefsome_function()do# body omittedend# preferreddefsome_function(arg1,arg2)do# body omittedenddefsome_functiondo# body omittedend
  • Usedo: for single lineif/unless statements.[link]

    # preferredifsome_condition,do:# some_stuff
  • Never useunless withelse.Rewrite these with the positive case first.[link]

    # not preferredunlesssuccessdoIO.puts('failure')elseIO.puts('success')end# preferredifsuccessdoIO.puts('success')elseIO.puts('failure')end
  • Usetrue as the last condition of thecond special form when you need aclause that always matches.[link]

    # not preferredconddo1+2==5->"Nope"1+3==5->"Uh, uh":else->"OK"end# preferredconddo1+2==5->"Nope"1+3==5->"Uh, uh"true->"OK"end
  • Use parentheses for calls to functions with zero arity, so they can bedistinguished from variables.Starting in Elixir 1.4, the compiler will warn you aboutlocations where this ambiguity exists.[link]

    defpdo_stuff,do:...# not preferreddefmy_funcdo# is this a variable or a function call?do_stuffend# preferreddefmy_funcdo# this is clearly a function calldo_stuff()end

Naming

This guide follows theNaming Conventions from the Elixir docs,including the use ofsnake_case andCamelCase to describe the casingrules.

  • Usesnake_case for atoms, functions and variables.[link]

    # not preferred:"some atom":SomeAtom:someAtomsomeVar=5defsomeFunctiondo...end# preferred:some_atomsome_var=5defsome_functiondo...end
  • UseCamelCase for modules (keep acronyms like HTTP, RFC, XML uppercase).[link]

    # not preferreddefmoduleSomemoduledo...enddefmoduleSome_Moduledo...enddefmoduleSomeXmldo...end# preferreddefmoduleSomeModuledo...enddefmoduleSomeXMLdo...end
  • Functions that return a boolean (true orfalse) should be namedwith a trailing question mark.[link]

    defcool?(var)doString.contains?(var,"cool")end
  • Boolean checks that can be used in guard clauses should be named withanis_ prefix.For a list of allowed expressions, see theGuard docs.[link]

    defguardis_cool(var)whenvar=="cool"defguardis_very_cool(var)whenvar=="very cool"
  • Private functions should not have the same name as public functions.Also, thedef name anddefp do_name pattern is discouraged.

    Usually one can try to find more descriptive names focusing on the differences.[link]

    defsum(list),do:sum_total(list,0)# private functionsdefpsum_total([],total),do:totaldefpsum_total([head|tail],total),do:sum_total(tail,head+total)

Comments

  • Write expressive code and try to convey your program's intention throughcontrol-flow, structure and naming.[link]

  • Comments longer than a word are capitalized, and sentences use punctuation.Useone space after periods.[link]

    # not preferred# these lowercase comments are missing punctuation# preferred# Capitalization example# Use punctuation for complete sentences.
  • Limit comment lines to 100 characters.[link]

Comment Annotations

  • Annotations should usually be written on the line immediately above therelevant code.[link]

  • The annotation keyword is uppercase, and is followed by a colon and a space,then a note describing the problem.[link]

    # TODO: Deprecate in v1.5.defsome_function(arg),do:{:ok,arg}
  • In cases where the problem is so obvious that any documentation would beredundant, annotations may be left with no note.This usage should be the exception and not the rule.[link]

    start_task()# FIXMEProcess.sleep(5000)
  • UseTODO to note missing features or functionality that should be added at alater date.[link]

  • UseFIXME to note broken code that needs to be fixed.[link]

  • UseOPTIMIZE to note slow or inefficient code that may cause performanceproblems.[link]

  • UseHACK to note code smells where questionable coding practices were usedand should be refactored away.[link]

  • UseREVIEW to note anything that should be looked at to confirm it isworking as intended.For example:REVIEW: Are we sure this is how the client does X currently?[link]

  • Use other custom annotation keywords if it feels appropriate, but be sure todocument them in your project'sREADME or similar.[link]

Modules

  • Use one module per file unless the module is only used internally by anothermodule (such as a test).[link]

  • Usesnake_case file names forCamelCase module names.[link]

    # file is called some_module.exdefmoduleSomeModuledoend
  • Represent each level of nesting within a module name as a directory.[link]

    # file is called parser/core/xml_parser.exdefmoduleParser.Core.XMLParserdoend
  • List module attributes, directives, and macros in the following order:[link]

    1. @moduledoc
    2. @behaviour
    3. use
    4. import
    5. require
    6. alias
    7. @module_attribute
    8. defstruct
    9. @type
    10. @callback
    11. @macrocallback
    12. @optional_callbacks
    13. defmacro,defmodule,defguard,def, etc.

    Add a blank line between each grouping, and sort the terms (like module names)alphabetically.Here's an overall example of how you should order things in your modules:

    defmoduleMyModuledo@moduledoc"""  An example module  """@behaviourMyBehaviouruseGenServerimportSomethingimportSomethingElserequireIntegeraliasMy.Long.Module.NamealiasMy.Other.Module.Example@module_attribute:foo@other_attribute100defstruct[:name,params:[]]@typeparams::[{binary,binary}]@callbacksome_function(term):::ok|{:error,term}@macrocallbackmacro_name(term)::Macro.t()@optional_callbacksmacro_name:1@docfalsedefmacro__using__(_opts),do::no_op@doc"""  Determines when a term is `:ok`. Allowed in guards.  """defguardis_ok(term)whenterm==:ok@impltruedefinit(state),do:{:ok,state}# Define other functions here.end
  • Use the__MODULE__ pseudo variable when a module refers to itself. Thisavoids having to update any self-references when the module name changes.[link]

    defmoduleSomeProject.SomeModuledodefstruct[:name]defname(%__MODULE__{name:name}),do:nameend
  • If you want a prettier name for a module self-reference, set up an alias.[link]

    defmoduleSomeProject.SomeModuledoalias__MODULE__,as:SomeModuledefstruct[:name]defname(%SomeModule{name:name}),do:nameend
  • Avoid repeating fragments in module names and namespaces.This improves overall readability andeliminatesambiguous aliases.[link]

    # not preferreddefmoduleTodo.Tododo...end# preferreddefmoduleTodo.Itemdo...end

Documentation

Documentation in Elixir (when read either iniex withh or generated withExDoc) uses theModule Attributes@moduledoc and@doc.

  • Always include a@moduledoc attribute in the line right afterdefmodule inyour module.[link]

    # not preferreddefmoduleAnotherModuledouseSomeModule@moduledoc"""  About the module  """...end# preferreddefmoduleAThirdModuledo@moduledoc"""  About the module  """useSomeModule...end
  • Use@moduledoc false if you do not intend on documenting the module.[link]

    defmoduleSomeModuledo@moduledocfalse...end
  • Separate code after the@moduledoc with a blank line.[link]

    # not preferreddefmoduleSomeModuledo@moduledoc"""  About the module  """useAnotherModuleend# preferreddefmoduleSomeModuledo@moduledoc"""  About the module  """useAnotherModuleend
  • Use heredocs with markdown for documentation.[link]

    # not preferreddefmoduleSomeModuledo@moduledoc"About the module"enddefmoduleSomeModuledo@moduledoc"""  About the module  Examples:  iex> SomeModule.some_function  :result  """end# preferreddefmoduleSomeModuledo@moduledoc"""  About the module  ## Examples      iex> SomeModule.some_function      :result  """end

Typespecs

Typespecs are notation for declaring types and specifications, fordocumentation or for the static analysis tool Dialyzer.

Custom types should be defined at the top of the module with the otherdirectives (seeModules).

  • Place@typedoc and@type definitions together, and separate eachpair with a blank line.[link]

    defmoduleSomeModuledo@moduledocfalse@typedoc"The name"@typename::atom@typedoc"The result"@typeresult::{:ok,term}|{:error,term}...end
  • If a union type is too long to fit on a single line, put each part of thetype on a separate line, indented one level past the name of the type.[link]

    # not preferred@typelong_union_type::some_type|another_type|some_other_type|one_more_type|a_final_type# preferred@typelong_union_type::some_type|another_type|some_other_type|one_more_type|a_final_type
  • Name the main type for a modulet, for example: the type specification for astruct.[link]

    defstruct[:name,params:[]]@typet::%__MODULE__{name:String.t()|nil,params:Keyword.t()}
  • Place specifications right before the function definition,after the@doc,without separating them by a blank line.[link]

    @doc"""Some function description."""@specsome_function(term)::resultdefsome_function(some_data)do{:ok,some_data}end

Structs

  • Use a list of atoms for struct fields that default tonil, followed by theother keywords.[link]

    # not preferreddefstructname:nil,params:nil,active:true# preferreddefstruct[:name,:params,active:true]
  • Omit square brackets when the argument of adefstruct is a keyword list.[link]

    # not preferreddefstruct[params:[],active:true]# preferreddefstructparams:[],active:true# required - brackets are not optional, with at least one atom in the listdefstruct[:name,params:[],active:true]
  • If a struct definition spans multiple lines, put each element on its own line,keeping the elements aligned.[link]

    defstructfoo:"test",bar:true,baz:false,qux:false,quux:1

    If a multiline struct requires brackets, format it as a multiline list:

    defstruct[:name,params:[],active:true]

Exceptions

  • Make exception names end with a trailingError.[link]

    # not preferreddefmoduleBadHTTPCodedodefexception[:message]enddefmoduleBadHTTPCodeExceptiondodefexception[:message]end# preferreddefmoduleBadHTTPCodeErrordodefexception[:message]end
  • Use lowercase error messages when raising exceptions, with no trailingpunctuation.[link]

    # not preferredraiseArgumentError,"This is not valid."# preferredraiseArgumentError,"this is not valid"

Collections

  • Always use the special syntax for keyword lists.[link]

    # not preferredsome_value=[{:a,"baz"},{:b,"qux"}]# preferredsome_value=[a:"baz",b:"qux"]
  • Use the shorthand key-value syntax for maps when all of the keys are atoms.[link]

    # not preferred%{:a=>1,:b=>2,:c=>0}# preferred%{a:1,b:2,c:3}
  • Use the verbose key-value syntax for maps if any key is not an atom.[link]

    # not preferred%{"c"=>0,a:1,b:2}# preferred%{:a=>1,:b=>2,"c"=>0}

Strings

  • Match strings using the string concatenator rather than binary patterns:[link]

    # not preferred<<"my"::utf8,_rest::bytes>>="my string"# preferred"my"<>_rest="my string"

Regular Expressions

No guidelines for regular expressions have been added yet.

Metaprogramming

  • Avoid needless metaprogramming.[link]

Testing

  • When writingExUnit assertions, put the expression being tested to the leftof the operator, and the expected result to the right, unless the assertion isa pattern match.[link]

    # preferredassertactual_function(1)==true# not preferredasserttrue==actual_function(1)# required - the assertion is a pattern matchassert{:ok,expected}=actual_function(3)

Resources

Alternative Style Guides

Tools

Refer toAwesome Elixir for libraries and tools that can helpwith code analysis and style linting.

Getting Involved

Contributing

It's our hope that this will become a central hub for community discussion onbest practices in Elixir.Feel free to open tickets or send pull requests with improvements.Thanks in advance for your help!

Check thecontributing guidelines for more information.

Spread the Word

A community style guide is meaningless without the community's support. Pleasetweet,star, and let any Elixir programmer knowaboutthis guide so they can contribute.

Copying

License

Creative Commons LicenseThis work is licensed under aCreative Commons Attribution 3.0 Unported License

Attribution

The structure of this guide, bits of example code, and many of the initialpoints made in this document were borrowed from theRuby community style guide.A lot of things were applicable to Elixir and allowed us to getsome documentout quicker to start the conversation.

Here's thelist of people who have kindly contributed to thisproject.

About

A community driven style guide for Elixir

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors52


[8]ページ先頭

©2009-2025 Movatter.jp