Git Product home page Git Product logo

elixir_style_guide's Introduction

Table of Contents

Prelude

Liquid architecture. It's like jazz — you improvise, you work together, you play 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 the Elixir programming language. Please feel free to make pull requests and suggestions, and be a part of Elixir's vibrant community.

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

Translations of the guide are available in the following languages:

Formatting

Elixir v1.6 introduced a Code Formatter and Mix 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, but are 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 by default, Windows users have to be extra careful). [link]

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

    git config --global core.autocrlf true
  • 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 proper use 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"], fn num -> IO.puts(num) end)
  • Do not use spaces after non-word operators that only take one argument; or around the range operator. [link]

    0 - 1 == -1
    ^pinned = some_func()
    5 in 1..10
  • Use blank lines between defs to break up a function into logical paragraphs. [link]

    def some_function(some_data) do
      some_data |> other_function() |> List.first()
    end
    
    def some_function do
      result
    end
    
    def some_other_function do
      another_result
    end
    
    def a_longer_function do
      one
      two
    
      three
      four
    end
  • Don't put a blank line after defmodule. [link]

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

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

    When the do: clause starts on its own line, treat it as a multiline function by separating it with blank lines.

    # not preferred
    def some_function([]), do: :empty
    def some_function(_),
      do: :very_long_line_here
    
    # preferred
    def some_function([]), do: :empty
    
    def some_function(_),
      do: :very_long_line_here
  • Add a blank line after a multiline assignment as a visual cue that the assignment is 'over'. [link]

    # not preferred
    some_string =
      "Hello"
      |> String.downcase()
      |> String.trim()
    another_string <> some_string
    
    # preferred
    some_string =
      "Hello"
      |> String.downcase()
      |> String.trim()
    
    another_string <> some_string
    # also not preferred
    something =
      if x == 2 do
        "Hi"
      else
        "Bye"
      end
    String.downcase(something)
    
    # preferred
    something =
      if x == 2 do
        "Hi"
      else
        "Bye"
      end
    
    String.downcase(something)
  • If a list, map, or struct spans multiple lines, put each element, as well as the 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 same line as the assignment. [link]

    # not preferred
    list =
    [
      :first_item,
      :second_item
    ]
    
    # preferred
    list = [
      :first_item,
      :second_item
    ]
  • If any case or cond clause needs more than one line (due to line length, multiple expressions in the clause body, etc.), use multi-line syntax for all clauses, and separate each one with a blank line. [link]

    # not preferred
    case arg do
      true -> IO.puts("ok"); :ok
      false -> :error
    end
    
    # not preferred
    case arg do
      true ->
        IO.puts("ok")
        :ok
      false -> :error
    end
    
    # preferred
    case arg do
      true ->
        IO.puts("ok")
        :ok
    
      false ->
        :error
    end
  • Place comments above the line they comment on. [link]

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

    #not preferred
    String.first(some_string)
    
    # preferred
    String.first(some_string)

Indentation

  • Indent and align successive with clauses. Put the do: 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 the with expression has a do block with more than one line, or has an else 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 preferred
    some_string |> String.downcase |> String.trim
    
    # preferred
    some_string |> String.downcase() |> String.trim()
  • Never put a space between a function name and the opening parenthesis. [link]

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

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

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

The Guide

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

Expressions

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

    def some_function(nil), do: {:error, "No Value"}
    def some_function([]), do: :ok
    
    def some_function([first | rest]) do
      some_function(rest)
    end
  • If you have more than one multiline def, do not use single-line defs. [link]

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

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

    While this is the preferred method, take into account that copy-pasting multiline pipelines into IEx might result in a syntax error, as IEx will evaluate 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 preferred
    some_string |> String.downcase()
    
    System.version() |> Version.parse()
    
    # preferred
    String.downcase(some_string)
    
    Version.parse(System.version())
  • Use bare variables in the first part of a function chain. [link]

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

    # not preferred
    def some_function arg1, arg2 do
      # body omitted
    end
    
    def some_function() do
      # body omitted
    end
    
    # preferred
    def some_function(arg1, arg2) do
      # body omitted
    end
    
    def some_function do
      # body omitted
    end
  • Use do: for single line if/unless statements. [link]

    # preferred
    if some_condition, do: # some_stuff
    
  • Never use unless with else. Rewrite these with the positive case first. [link]

    # not preferred
    unless success do
      IO.puts('failure')
    else
      IO.puts('success')
    end
    
    # preferred
    if success do
      IO.puts('success')
    else
      IO.puts('failure')
    end
  • Use true as the last condition of the cond special form when you need a clause that always matches. [link]

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

    defp do_stuff, do: ...
    
    # not preferred
    def my_func do
      # is this a variable or a function call?
      do_stuff
    end
    
    # preferred
    def my_func do
      # this is clearly a function call
      do_stuff()
    end

Naming

This guide follows the Naming Conventions from the Elixir docs, including the use of snake_case and CamelCase to describe the casing rules.

  • Use snake_case for atoms, functions and variables. [link]

    # not preferred
    :"some atom"
    :SomeAtom
    :someAtom
    
    someVar = 5
    
    def someFunction do
      ...
    end
    
    # preferred
    :some_atom
    
    some_var = 5
    
    def some_function do
      ...
    end
  • Use CamelCase for modules (keep acronyms like HTTP, RFC, XML uppercase). [link]

    # not preferred
    defmodule Somemodule do
      ...
    end
    
    defmodule Some_Module do
      ...
    end
    
    defmodule SomeXml do
      ...
    end
    
    # preferred
    defmodule SomeModule do
      ...
    end
    
    defmodule SomeXML do
      ...
    end
  • Functions that return a boolean (true or false) should be named with a trailing question mark. [link]

    def cool?(var) do
      String.contains?(var, "cool")
    end
  • Boolean checks that can be used in guard clauses should be named with an is_ prefix. For a list of allowed expressions, see the Guard docs. [link]

    defguard is_cool(var) when var == "cool"
    defguard is_very_cool(var) when var == "very cool"
  • Private functions should not have the same name as public functions. Also, the def name and defp do_name pattern is discouraged.

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

    def sum(list), do: sum_total(list, 0)
    
    # private functions
    defp sum_total([], total), do: total
    defp sum_total([head | tail], total), do: sum_total(tail, head + total)

Comments

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

  • Comments longer than a word are capitalized, and sentences use punctuation. Use one 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 the relevant 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.
    def some_function(arg), do: {:ok, arg}
  • In cases where the problem is so obvious that any documentation would be redundant, annotations may be left with no note. This usage should be the exception and not the rule. [link]

    start_task()
    
    # FIXME
    Process.sleep(5000)
  • Use TODO to note missing features or functionality that should be added at a later date. [link]

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

  • Use OPTIMIZE to note slow or inefficient code that may cause performance problems. [link]

  • Use HACK to note code smells where questionable coding practices were used and should be refactored away. [link]

  • Use REVIEW to note anything that should be looked at to confirm it is working 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 to document them in your project's README or similar. [link]

Modules

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

  • Use snake_case file names for CamelCase module names. [link]

    # file is called some_module.ex
    
    defmodule SomeModule do
    end
  • Represent each level of nesting within a module name as a directory. [link]

    # file is called parser/core/xml_parser.ex
    
    defmodule Parser.Core.XMLParser do
    end
  • 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:

    defmodule MyModule do
      @moduledoc """
      An example module
      """
    
      @behaviour MyBehaviour
    
      use GenServer
    
      import Something
      import SomethingElse
    
      require Integer
    
      alias My.Long.Module.Name
      alias My.Other.Module.Example
    
      @module_attribute :foo
      @other_attribute 100
    
      defstruct [:name, params: []]
    
      @type params :: [{binary, binary}]
    
      @callback some_function(term) :: :ok | {:error, term}
    
      @macrocallback macro_name(term) :: Macro.t()
    
      @optional_callbacks macro_name: 1
    
      @doc false
      defmacro __using__(_opts), do: :no_op
    
      @doc """
      Determines when a term is `:ok`. Allowed in guards.
      """
      defguard is_ok(term) when term == :ok
    
      @impl true
      def init(state), do: {:ok, state}
    
      # Define other functions here.
    end
  • Use the __MODULE__ pseudo variable when a module refers to itself. This avoids having to update any self-references when the module name changes. [link]

    defmodule SomeProject.SomeModule do
      defstruct [:name]
    
      def name(%__MODULE__{name: name}), do: name
    end
  • If you want a prettier name for a module self-reference, set up an alias. [link]

    defmodule SomeProject.SomeModule do
      alias __MODULE__, as: SomeModule
    
      defstruct [:name]
    
      def name(%SomeModule{name: name}), do: name
    end
  • Avoid repeating fragments in module names and namespaces. This improves overall readability and eliminates ambiguous aliases. [link]

    # not preferred
    defmodule Todo.Todo do
      ...
    end
    
    # preferred
    defmodule Todo.Item do
      ...
    end

Documentation

Documentation in Elixir (when read either in iex with h or generated with ExDoc) uses the Module Attributes @moduledoc and @doc.

  • Always include a @moduledoc attribute in the line right after defmodule in your module. [link]

    # not preferred
    
    defmodule AnotherModule do
      use SomeModule
    
      @moduledoc """
      About the module
      """
      ...
    end
    
    # preferred
    
    defmodule AThirdModule do
      @moduledoc """
      About the module
      """
    
      use SomeModule
      ...
    end
  • Use @moduledoc false if you do not intend on documenting the module. [link]

    defmodule SomeModule do
      @moduledoc false
      ...
    end
  • Separate code after the @moduledoc with a blank line. [link]

    # not preferred
    defmodule SomeModule do
      @moduledoc """
      About the module
      """
      use AnotherModule
    end
    
    # preferred
    defmodule SomeModule do
      @moduledoc """
      About the module
      """
    
      use AnotherModule
    end
  • Use heredocs with markdown for documentation. [link]

    # not preferred
    defmodule SomeModule do
      @moduledoc "About the module"
    end
    
    defmodule SomeModule do
      @moduledoc """
      About the module
    
      Examples:
      iex> SomeModule.some_function
      :result
      """
    end
    
    # preferred
    defmodule SomeModule do
      @moduledoc """
      About the module
    
      ## Examples
    
          iex> SomeModule.some_function
          :result
      """
    end

Typespecs

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

Custom types should be defined at the top of the module with the other directives (see Modules).

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

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

    # not preferred
    @type long_union_type ::
            some_type | another_type | some_other_type | one_more_type | a_final_type
    
    # preferred
    @type long_union_type ::
            some_type
            | another_type
            | some_other_type
            | one_more_type
            | a_final_type
  • Name the main type for a module t, for example: the type specification for a struct. [link]

    defstruct [:name, params: []]
    
    @type t :: %__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.
    """
    @spec some_function(term) :: result
    def some_function(some_data) do
      {:ok, some_data}
    end

Structs

  • Use a list of atoms for struct fields that default to nil, followed by the other keywords. [link]

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

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

    defstruct foo: "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 trailing Error. [link]

    # not preferred
    defmodule BadHTTPCode do
      defexception [:message]
    end
    
    defmodule BadHTTPCodeException do
      defexception [:message]
    end
    
    # preferred
    defmodule BadHTTPCodeError do
      defexception [:message]
    end
  • Use lowercase error messages when raising exceptions, with no trailing punctuation. [link]

    # not preferred
    raise ArgumentError, "This is not valid."
    
    # preferred
    raise ArgumentError, "this is not valid"

Collections

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

    # not preferred
    some_value = [{:a, "baz"}, {:b, "qux"}]
    
    # preferred
    some_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 writing ExUnit assertions, put the expression being tested to the left of the operator, and the expected result to the right, unless the assertion is a pattern match. [link]

    # preferred
    assert actual_function(1) == true
    
    # not preferred
    assert true == actual_function(1)
    
    # required - the assertion is a pattern match
    assert {:ok, expected} = actual_function(3)

Resources

Alternative Style Guides

Tools

Refer to Awesome Elixir for libraries and tools that can help with code analysis and style linting.

Getting Involved

Contributing

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

Check the contributing guidelines for more information.

Spread the Word

A community style guide is meaningless without the community's support. Please tweet, star, and let any Elixir programmer know about this guide so they can contribute.

Copying

License

Creative Commons License This work is licensed under a Creative Commons Attribution 3.0 Unported License

Attribution

The structure of this guide, bits of example code, and many of the initial points made in this document were borrowed from the Ruby community style guide. A lot of things were applicable to Elixir and allowed us to get some document out quicker to start the conversation.

Here's the list of people who have kindly contributed to this project.

elixir_style_guide's People

Contributors

alxndr avatar arialblack14 avatar christopheradams avatar corroded avatar deby22 avatar edmz avatar eksperimental avatar furikuri avatar gabrielpra1 avatar gazler avatar geekerzp avatar ihildebrandt avatar kosmas avatar marocchino avatar moxley avatar mracos avatar mschae avatar mtarnovan avatar nessamurmur avatar odaeus avatar pdilyard avatar qqwy avatar rhruiz avatar rob05c avatar rrrene avatar simonprev avatar tbk145 avatar victorlcampos avatar whatyouhide avatar wraiford avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elixir_style_guide's Issues

About comparison testing

I read the Elixir code and found that the following two styles are used at the same time:

assert expected == actual
assert actual == expected

I'd like to unify for consistency, but what do you think?

Indentation on pipelines that bind a variable

In the current style guide, the only real commentary on pipelines is:

# Multiline pipelines use a single level of indentation.
some_string
|> String.downcase
|> String.strip

(sidenote: "single level of indentation" here seems to imply no indentation, unless I'm missing something?)

But what about when the result of that pipelining should be bound to a variable? Like

result = some_string
|> String.downcase
|> String.strip

I've found myself indenting the pipelines by two spaces to give a little bit of clarity, like so:

result = some_string
  |> String.downcase
  |> String.strip

but I'm curious if anyone else has a take on this? Or is binding the result of a pipeline frowned upon for some reason?

Ambiguity about parens in pipelines

The style guide states:

  • Use parentheses when you have arguments, no parentheses when you don't.

Later, it states:

  • Use parentheses in function calls, especially inside a pipeline.
# preferred
f(3) |> g()

I'm unclear about whether the parens in g() are intentional (I don't believe there's any parsing ambiguity). If they're intentional, I suggest revising the language to say Use parentheses in function calls within a pipeline, even without arguments. However, if that's just an oversight the example should be f(3) |> g.

I'm happy to PR the change if someone can clarify the intent.

I'd love to see a conventional format for the "with" keyword

I've seen:

with {:ok, user} <- User.get(id),
     {:ok, posts} <- Post.by_author(user)
do
  {:ok, posts}
else
  error -> error
end
with \
  {:ok, user} <- User.get(id),
  {:ok, posts} <- Post.by_author(user)
do
  {:ok, posts}
else
  error -> error
end

amongst others including using the do: form of the block, although I'm not sure if that format works with an else clause as well.

Structs

Great initiative with a style guide!

I'd like some directions in the guide on writing structs, both when doing defstruct and when initializing them using %StructName{...}. I'm new to Elixir so I don't have much to say here. Some scenarios that I've though about is when declaring och using structs and splitting it on multiple lines. Should the continuations be aligned with the first key/expression or just 2 spaces in?

Multiline pipeline syntax

Consider the codes below:

File.stream!("path/to/some/file", read_ahead: 100_000) # NEW!
|> Flow.from_enumerable()
|> Flow.flat_map(fn line ->
    for word <- String.split(empty_space), do: {word, 1}
   end)
|> Flow.partition_with(storage: :ets) # NEW!
|> Flow.reduce_by_key(& &1 + &2)
|> Enum.to_list()

Notice the pattern in this bit:

Flow.flat_map(fn line ->
  for word <- String.split(empty_space), do: {word, 1}
end)

for is only indented by two spaces, and end) is indented on the line as Flow

I have seen many different ways on how they were written, but this seems to be cleanest way.

Ref: http://elixir-lang.org/blog/2016/07/14/announcing-genstage/

Challenge single level of indentation for multiline pipes

I'd like to challenge this:

# Multiline pipelines use a single level of indentation.
some_string
|> String.downcase
|> String.strip

with:

# Multiline pipes should be indented.
some_string
  |> String.downcase
  |> String.strip

I realize that the first version is something that has been used pretty much everywhere but I believe it is misleading to the eye. Indentation implies a relationship to the outdented value, not indenting could imply there is no relationship.

Thoughts?

EditorConfig

I have not used it myself yet, but I know many (big) projects are using it, and it seems a great idea.
http://editorconfig.org/

a .editorconfig file that can set stuff like

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

#4 space indentation
[*.ex, *.exs, .travis.yml, *.md]
indent_style = space
indent_size = 2

and many more such as charset, trim_trailing_whitespace, insert_final_newline

and it is supported by default by the github code editor and viewer, so that's a bonus when people want to contribute also for this guide.

I'm wondering if anybody out there is using it for any elixir project.

So my proposal are:

  • Should we include it in the list of tools?
  • Should we include use it in this project?
  • Should we recommend its usage?

Good line length?

Should there be a line length limit? I have my editor set up to make text after 80 chars a vivid red. I think there also needs to be guidelines for wrapping function definitions:

# This is too long...
def handle_info(:run_build_script, state = %{config: %{repo: repo, script: scr, git_cmd: git_cmd}}) do
...
end

A few things are missing

First, you say to put spaces around operators. There was no mention of ranges, they use '..' which can be seen as an operator, yet I don't think most people put spaces around those.

Then there's '::'. They're present in specs (to specify the return type) and binaries. Should we surround them with spaces in both cases?

Maps and structs. If I understand correctly, tuples must be written {:as, :such}, however, does it still stand for maps and structs? %Point{x: 0, y: 0} seems a bit "crowded" to me, but I've seen it in multiple repos.

List patterns, head and tail. I've seen both [head|tail] and [head | tail], and I'm not sure which one to use. The second one looks cleaner to me, and it fits with the rule that says to put spaces around operators. You've used [first|rest] in your examples.

I probably forgot a few other things, but this is all I can think of right now.

Bad/Good Language

Probably better to use "Preferred"/"Not Preferred" or something similar.

Add suggestion of :default in cond default case

In the section, "An atom can be used as a catch-all expression in a cond as it evaluates to a truthy value. Suggested atoms are :else or :otherwise"

suggestion of :default can also be added.

default is also the catchall condition in C switch statements, and should be familiar.

Use of IO.puts should have brackets

There are a number of examples of use of IO.puts "A string value", but by this style guide functions with arguments should make use of brackets.
Therefore I think the correct styling should be IO.puts("A string value")

Convention for naming a private method

I was wondering how to name a private method especially in a situation where it has the exact same intention but accepts some additional parameters(e.g.: accumulator)?

For example:

defmodule Factors do
  def factors n do
    _factors HashSet.new, n, n
  end

  defp _factors set, _, 0 do
    set
  end
  defp _factors set, n, x do
    if rem(n, x) == 0 do
      _factors HashSet.put(set, x), n, (x-1)
    else
      _factors set, n, (x-1)
    end
  end
end

Keyword Wrapping (`[]` Brackets)

As discussed in #113, there is no guidance regarding when keywords should be wrapped in square brackets and when they shouldn't be.

With regards to defstruct, it seems that the same source material referred to in #113 establishes a precedent that keywords used in defstruct declarations should not be wrapped in square brackets. The referenced sources:

Multiline pipe wording ambiguity?

"# Multiline pipelines use a single level of indentation." but the pipe is not indented further than the previous line. Would "Multiline pipelines do not change indentation" or "Multiline pipelines do not indent further" be clearer, perhaps?

# Multiline pipelines do not change indentation
foo
|> do_something
|> do_something_else

Naming functions with verbs

This is something I've been debating in my code. In OOP you have objects that combine state and behavior, so methods on those objects can be named like user.full_name or struct.byte_size, etc. In functional your data has no behavior, so it's always operated upon: User.full_name(my_user), Struct.byte_size(my_struct). However, this reads weird. In this configuration functions should say something like User.get_full_name(my_user), or Struct.calc_byte_size(my_struct). In various code examples I see that the verb tends to be omitted, which also makes sense, because if it wasn't, every function would be prepended with things like get_, and calc_, which creates noise. So should we consider the verb implicit and just omit it? Or maybe have a convention where we omit it only in cases where it would be something very generic like get_ or calc_?

Where does defstruct go in the list?

There's a nice list now to show the preferred ordering of things:

defmodule MyModule do
  @moduledoc """
  An example module
  """

  use GenServer
  import Something
  import SomethingElse
  alias My.Long.Module.Name
  alias My.Other.Module.Name
  require Integer

  @type params :: [{binary, binary}]

  @module_attribute :foo
  @other_attribute 100

  ...
end

But where does defstruct go in this list? Since it's pretty tightly bound with the module, I figure it should go somewhere near the top, but I could see the defaults needing/wanting to use aliases, or perhaps the module attributes... I hesitate to put it too far "down" the list because then it may not be so obvious that the module is actually a struct... Suggestions?

Some minor remarks

The code Enum.map( ["one", "two", "three"], fn ( num ) -> IO.puts num end
needs an end ) to work, should be:
Enum.map( ["one", "two", "three"], fn ( num ) -> IO.puts num end )

The example in Syntax:

good

def some_method(arg1, arg2)
#body omitted
end

def some_method

body omitted

end
is not in agreement with: ....but run different clauses that match for the same function together , from the previous §; the blank line between them should be removed.

Cheers,
Ivo

remove use of PascalCase

#81 has introduced the PascalCase terminology replacing CamelCase as we know it in Elixir.

Please read the discussion there.
I think the PascalCase definition is vague, and the only strong definition of it is adopted mostly by Microsoft, and more over, use of CamelCase terminology in Elixir language is clear, and PascalCase is non existent.
And including it in the style-code is a source of confusion.
IMO 6cbee59 should be reverted until Elixir language itself adopts such terminology --if that ever happens--

Is __MODULE__ preferable?

defmodule SomeNamespace.SomeModuleName do
  defstruct name: nil
  @type t :: %__MODULE__{name: String.t}

  def new, do: %__MODULE__{}
end

What do you think about using __MODULE__ as it is used in new method.
(Already saw @type t :: %__MODULE__ in Elixir sources and that looks good.) And in general what about a recommendation to use __MODULE__ through whole module against to manual qualification of the name like SomeNamespace.SomeModuleName. Hope I was clean enough (:

Review, revise, or remove run-on defs

There is a rule under Source Code Layout which reads:

  • ...but run single-line defs that match for the same function together.

    def some_function(nil), do: {:err, "No Value"}
    def some_function([]), do: :ok
    def some_function([first|rest]) do
      some_function(rest)
    end

I'm curious how applicable this is or if there are any edge cases or other issues or problems it causes. Should it be amended? Does anyone have counter-examples?

Thanks to @DavidAntaramian for bringing this up on another channel.

Blank line after multiline pipe

After you use a multiline pipe to set a result leave a blank line to as a visual marker that the pipe
is "over".

This :

inner =
  args
  |> Enum.map(fn(tp) -> format_doc_arg(tp) end)
  |> Enum.join(", ")

"("<>inner<>")"

Not This:

inner =
  args
  |> Enum.map(fn(tp) -> format_doc_arg(tp) end)
  |> Enum.join(", ")
"("<>inner<>")"

Multi-line arg list in function calls

There is currently no guideline on function calls with multi-line arg lists. For use-cases I encountered most often, following rules works for me:

# call with parentheses, first argument in-line with function
some_func(:first_arg,
          :second_arg,
          :third_arg)

# call with parentheses, first argument in new line
some_funct(
  :somewhat_long_expression_for_argument,
  :second_arg,
  :third_arg)

# call without parentheses and keyword list
some_funct :regular_arg,
  keyword_arg: "option",
  other_keyword_arg: "there"

I am aware that third example (without parentheses) is conflicting with Use parentheses in function calls, however I find it quite verbose in various configuration-related code with long keyword lists.

Thoughts? Suggestions?

functions with long guard clause

There's no guide for a function which has a long guard clause.
How Ecto project is doing:

  def escape({{:., _, [{var, _, context}, field]}, _, []}, _type, params, vars, _env)
      when is_atom(var) and is_atom(context) and is_atom(field) do
    {escape_field(var, field, vars), params}
  end

Using singular or plural when naming a nested directory

I'm just working on a project where I have several modules that implement supervisors, several modules that implement registries for other modules and several modules that implement queues.
Basically I can name the directories queues, registries and supervisors or queue, registry and supervisor(the choice is between plural and singular).

I've went to check other elixir projects to see what convention they used( like Phoenix and Elixir) and I've noticed that most directory names are singular. But after reading the names of the modules in these directories I feel that in those cases it makes sense to use the singular form because the modules inside that directory are components of the thing that the directory name refers to( for example in the Elixir Kernel it makes perfect sense to use singular for the kernel directory because all the modules inside are components of it).

In my specific case I would have in the directory for queues modules named requests_queue.ex and responses_queue.ex, both different are two different types of queues, and not components of a queue functionality, therefore I think it would make sense to use the plural form queues for naming the directory( in this specific case). The downside might be that some people might consider it confusing to have both singular and plural used to name directories.

What do you think?

Rationale for choices

One of the things I discussed with Christoper earlier via Slack is that I didn't agree with some of the positions taken in the style guide. That's not a criticism of the guide; in fact, I think that the chain of maintainers for this guide has gone to great lengths to ensure that the guide is community driven.

However, I think that the guide can be improved by presenting the rationale of why the guide adopts each particular choice it does. That way, when someone expresses disagreement, they can present a counter-argument to the current choice that can be evaluated by the community. That will make future discussions like #116 easier since the evaluation happens without having to dive through "why was this decision made in the first place; what was the rationale?"

Swinging to the opposite side, I think it would be good to have a note at the top stating that we are presenting a community-reviewed set of options that allow for cohesion and a starting point. I realize that's partially addressed in the "Getting Involved" section, but I just think it would be good for people to see it when they first start reading the guide. We're not intending to create solemn law that must not be violated upon pain of exile. Or as I was telling @christopheradams, this "is more what you call guidelines than actual rules."

reword "NOTE"

this is what we have now:

NOTE: From here on out, you get a PR merged, you get added as a
collaborator. People who have PRs merged have been added as collaborators.

I think we need some rewording here.
it's a bit too hard the imperative mode.
I will go with something more mellow as

NOTE: When you submit a PR and it gets merged, you are automatically added as a collaborator If you would like to be removed, please let us know.

I don't know what is the way of communicating, but there should be one.

Blank line between use/import/alias/require groups

I think it is both more readable and prettier to separate use/import/alias/require groups with a blank line.

# preferred
defmodule MyModule do
  use Xenserver

  import Something
  import SomethingElse

  alias My.Long.Module.Name
  alias My.Other.Module.Name

  require Integer

  ...
end

# not preferred
defmodule MyModule do
  use Xenserver
  import Something
  import SomethingElse
  alias My.Long.Module.Name
  alias My.Other.Module.Name
  require Integer

  ...
end

Translation

Hi,
It would be nice with it's possible translate this guide. I'm member from phoenixframework-Brazil. We are translating the phoenix guide and we can help translate this style guide for portuguese.

Testing best practices (especially around doctests)

I'm pretty new to Elixir and I'm building my first open source package. Obviously I'm aiming for good test coverage, but I'm not sure where to draw the line between using doctests in my modules, and using normal tests in test scripts.

Is there a convention for this? Can/should this be added to the style guide?

Brackets for multiline lists

What is the preferred way of placing brackets for the lists which span multiple lines:
1.

list = [
  :first_item,
  :second_item,
]
list = 
[
  :first_item,
  :second_item,
]
list = 
  [:first_item,
   :second_item]

Question about style rule

Hi,

I am quite new to Elixir, so excuse me if I missed something.

Enum.map( [1, 2, 3], fn ( num ) -> IO.puts num end
1) this code doesn't seem to work in iex
when adding a ) at the end:
(FunctionClauseError) no function clause matching in IO.puts/2

   2) the spaces in fn ( num ) do they not contradict with the rule:
    Never put a space between a method name and the opening parenthesis.

Cheers,
Ivo

Empty line after module header?

Any suggestions about having/not having an empty line between module header and first function?
And could we add it to the guide?

So which one should be preferred?

defmodule some_module do

def some_function do
# function body
end
end

or

defmodule some_module do
def some_function do
# function body
end
end

Use functions instead of methods

Strictly speaking, Elixir does not have methods, we have functions. The guide should likely replace "method" by "function" throughout.

Best practice: How and where to put protocols and their implementation?

I think it would help to have a common place to put protocol definitions and also implementations. It would be in particular interesting if protocols are implemented for existing modules.

For discussion:

definition:

file: lib/protocols/incredible.ex

implementation for a new modules within the module.

implementation for an existing module (e.g. Integer) in:

file: lib/protocols/impl/integer.ex

or

file: lib/protocols/integer_impl.ex

?

Linter preset?

Is there a good linter for implementing the details in this style guide (ideally one with a preset of some kind for the styles defined here)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.