elixir_inconsistencies icon indicating copy to clipboard operation
elixir_inconsistencies copied to clipboard

List of inconsistencies found throughout the Elixir programming language

Elixir Inconsistencies

The aim of this project is to document all inconsistencies found throughout the Elixir programming language. Providing a single page where all inconsistencies can be looked at, hoping that many could be fixed in next major release, and if not possible users can still be aware of them. Also a reference to look at when creating new stuff in the Elixir programming language project.

Convention

Spelling

  • Behaviour

    • Affects: @behaviour, Behaviour (hard-deprecated in v1.4).
    • Proposed: @behavior, Behavior.
    • Notes: While all modules, functions, attributes follow the American English spelling, this one in particular uses the British English spelling, being borrowed from Erlang.
  • Acronysms Acronysms should be spelled all in uppercase: ie. pid should be spelled PID.

Use of underscore

It is a convention to use underscores to separate words, but there are some exceptions:

  • def* Functions/macros starting with def do not follow this convention:

    • Affects: defdelegate/2, defexception/1, defimpl/2, defimpl/3, defmacro/1, defmacro/2, defmacrop/1, defmacrop/2, defmodule/2, defoverridable/1, defp/1, defp/2, defprotocol/2, defstruct/1.
  • Private functions, macros types p suffix meaning private does not use underscore.

    • Affects: defmacrop/1, defmacrop/2, defp/1, defp/2, @typep.
    • Proposed:
      • defmacro_p/1, defmacro_p/2, def_p/1, def_p/2, @type_p, or
      • defmacro_priv/1, defmacro_priv/2, def_priv/1, def_priv/2, @type_priv.
    • Notes: having no underscore and using only one letter (p), makes it difficult sometimes to differentiate from the public ones.

Elixir

Attributes

  • behaviour

    • Affects: @behaviour.
    • Proposed: @behavior.
    • Notes: See Spelling section of this guide for more information.
  • fallback_to_any

    • Affects: @fallback_to_any.
    • Proposed: @fall_back_on_any.
    • Notes: To "fall back" is a verb and it is two separate words. Fallback is one word when it is a noun. Additionally, the phrasal-verb is "to fall back on something", not "to fall back to something".
  • macrocallback

    • Affects: @macrocallback.
    • Proposed: @macro_callback.
    • Precedents: @optional_callbacks.

Modules and functions

  • Plural in module names: names are usually in singular.

    • Affects: Kernel.SpecialForms, Kernel.Utils, ExUnit.FailuresManifest, IO.ANSI.Docs, Inspect.Opts, List.Chars, List.Chars.*, Stream.Reducers, String.Chars, String.Chars.*, IEx.Helpers, Mix.Tasks.Compile.Protocols, Mix.Tasks.*, Mix.Utils, :elixir_aliases, :elixir_clauses, :elixir_erl_clauses, :elixir_errors.
    • Proposed: Kernel.SpecialForm, Kernel.Util, ExUnit.FailureManifest, IO.ANSI.Doc, Inspect.Opt, List.Char, List.Char.*, Stream.Reducer, String.Char, String.Char.*, IEx.Helper, Mix.Tasks.Compile.Protocol, Mix.Task.*, Mix.Util, :elixir_alias, :elixir_clause, :elixir_erl_clause, :elixir_error.
  • _with

    • Affected:
      • Enum.split_with/2.
    • Proposed:
      • Enum.split_by/2.
    • Precedents: Enum.chunk_by/2, Enum.dedup_by/2, Enum.group_by/3, Enum.max_by/3, Enum.min_by/3, Enum.min_max_by/3, Enum.sort_by/3, Enum.uniq_by/2,, Stream.chunk_by/2, Stream.dedup_by/2, Stream.uniq_by/2.
    • Rationale: The suffix "_with" in functions is only used in three functions: String.ends_with?/2, String.starts_with?/2 and List.starts_with?/2; and these are described in with content of elements. On the other hand, the suffix "_by" is used in the numerous functions mentioned in Precedents, and they are used to filter content based on the function provided.
  • foldl, foldr

    • Affects: List.foldl/3, List.foldr/3.
    • Proposed:
      • List.fold_left/3 and List.fold_right/3, or
      • List.fold_l/3 andList.fold_r/3.
    • Notes: These List.fold* functions come from Haskell, but it is weird to have "l" and "r" as "left" and "right", as they are not seen in it in other function names. Other functions use a single letter, but these are Unix command, and they refer to the short version of the argument, such as File.cp_r/3, File.rm_rf/1, File.ln_s/2. So I propose we use "_left" and "_right" to be more explicit, and to make a clear difference away from single letters that represent short arguments.
  • macrocallback

    • Affects: Behaviour.defmacrocallback/1.
    • Proposed: Behaviour.defmacro_callback/1.
    • Notes: Behaviour module has been hard-deprecated in v1.4.
  • opts

    • Affects:
      • Inspect.Opts, IEx.inspect_opts/0, Mix.Tasks.Test.formatter_opts/1.
      • Regex.opts/1, :opts field in Regex.t/0 type and %Regex{} struct.
    • Proposed:
      • Inspect.Options, Regex.options/1, IEx.inspect_options/0, Mix.Tasks.Test.formatter_options/1.
      • Regex.options/1, :options field in Regex.t/0 type and %Regex{} struct.
    • Proposed alternatively: Inspect.Option.
    • Precedents: OptionParser, Kernel.CLI.OptionParsingTest, Code.available_compiler_options/0, Code.compiler_options/0, Code.compiler_options/1, Mix.SCM.accepts_options/2.
  • whereis

    • Affects: GenServer.whereis/1, Process.whereis/1.
    • Proposed: GenServer.where_is/1, Process.where_is/1.
    • Notes: Borrowed from Erlang's :erlang.whereis/1.
  • IO.bin*

    • Affects: IO.binread/2, IO.binstream/2, IO.binwrite/2.
    • Proposed: IO.bin_read/2, IO.bin_stream/2, IO.bin_write/2.
    • Proposed alternatively: IO.binary_read/2, IO.binary_stream/2, IO.binary_write/2.
  • List.key*

    • keydelete

      • Affects: List.keydelete/3.
      • Proposed: List.delete_key/3.
      • Proposed alternatively: List.key_delete/3, List.Key.delete/3.
      • Precedents: Application.delete_env/3, Code.delete_path/1, Keyword.delete_first/2, List.delete_at/2, Module.delete_attribute/2, Process.delete/1, Supervisor.delete_child/2, System.delete_env/1, Tuple.delete_at/2, Process.get_keys/1, Dict.has_key?/2.
      • Notes: name was borrowed from Erlang's :lists.keydelete/3.
    • keyfind

      • Affects: List.keyfind/4.
      • Proposed: List.find_key/4.
      • Proposed alternatively: List.key_find/4, List.Key.find/4.
      • Precedents: Enum.find/3, Enum.find_index/2, Enum.find_value/3, System.find_executable/1, Task.find/2.
      • Notes: name was borrowed from Erlang's :lists.keyfind/4.
    • keymember?

      • Affects: List.keymember?/3.
      • Proposed: List.member_key?/3 or List.keymember?/3.
      • Proposed alternatively: List.Key.member?/3.
      • Notes: name was borrowed from Erlang's :lists.keymember?/3.
    • keyreplace

      • Affects: List.keyreplace/4.
      • Proposed: List.replace_key/4.
      • Proposed alternatively: List.key_replace/4 or List.Key.replace/4.
      • Notes: name was borrowed from Erlang's :lists.keydelete/3.
    • keysort

      • Affects: List.keysort/2.
      • Proposed: List.sort_key/2.
      • Proposed alternatively: List.key_sort/2 or List.Key.sort/2.
      • Notes: name was borrowed from Erlang's :lists.keysort/3.
    • keystore

      • Affects: List.keystore/4.
      • Proposed: List.store_key/4.
      • Proposed alternatively: List.key_store/4 or List.Key.store/4.
      • Notes: name was borrowed from Erlang's :lists.keystore/4.
    • keytake

      • Affects: List.keytake/3.
      • Proposed: List.take_key/3.
      • Proposed alternatively: List.key_take/3 or List.Key.take/3.
      • Notes: name was borrowed from Erlang's :lists.keytake/3.
  • Path.*name

    • Affects: all the Path.*name functions.

    • Notes: I can see this come from Unix/Linux functions, but only "basename" and "dirname" are, the rest just come from asimilating the uncommon naming convention.

    • absname

      • Affects: Path.absname/1, Path.absname/2.
      • Proposed: Path.abs_name/1, Path.abs_name/2.
      • Notes: name asimilated from basename and dirname Unix commands, but there is not such absname Unix command.
    • basename

      • Affects: Path.basename/1, Path.basename/2.
      • Proposed: Path.base_name/1, Path.base_name/2.
      • Notes: name borrowed from basename Unix commands.
    • dirname

      • Affects: Path.dirname/1.
      • Proposed: Path.dir_name/1.
      • Notes: name borrowed from dirname Unix commands.
    • extname

      • Affects: Path.extname/1.
      • Proposed: Path.extension_name/1.
      • Proposed alernatively: Path.ext_name/1.
      • Notes: name asimilated from basename and dirname Unix commands, but there is not such extname Unix command, therefore why the abbreviation is not preferred.
    • rootname

      • Affects: Path.rootname/1.
      • Proposed: Path.root_name/1.
      • Notes: name asimilated from basename and dirname Unix commands, but there is not such rootname Unix command.
  • Regex.opts/1 and :opts field in %Regex{}

    • Affects: Regex.opts/1 and :opts field in %Regex{}
    • Proposed: Regex.modifiers/1 and :modifiers field in %Regex{}.
    • Notes: It is referred to them as modifiers exclusively, it it weird to call them "opts".

Types

  • ansicode

    • Affects: IO.ANSI.ansicode/0.
    • Proposed: IO.ANSI.ansi_code/0.
    • Precedents: IEx.Config.ansi_docs/0, :ansi_enabled option in Elixir application.
  • ansidata

    • Affects: IO.ANSI.ansidata/0.
    • Proposed: IO.ANSI.ansi_data/0.
    • Precedents: IEx.Config.ansi_docs/0, :ansi_enabled option in Elixir application.
  • ansilist

    • Affects: IO.ANSI.ansilist/0.
    • Proposed: IO.ANSI.ansi_list/0.
    • Precedents: IEx.Config.ansi_docs/0, :ansi_enabled option in Elixir application.
  • information

    • Affects: Function.information/0.
    • Proposed: Function.info/0.
    • Precedents: Function.info/1,2, Port.info/1,2, System.build_info/0, System.info/0, Module.__info__/1 (callback), GenServer.handle_info/2 (callback), Process.info/1,2, GenEvent.handle_info/2 (callback).
  • iodata

    • Affects: iodata/0 built-in type.
    • Proposed: io_data/0.
    • Precedents: File.io_device and :std_io to represent standard I/O.
  • iolist

    • Affects: iolist/0 built-in type.
    • Proposed: io_list/0.
    • Precedents: File.io_device and :std_io to represent standard I/O.
  • nodata

    • Affects: IO.nodata/0.
    • Proposed: IO.no_data/0.
    • Precedents: no_return/0 built-in type, IO.ANSI.no_underline/0.
  • non_neg_number or nonempty_list/0

    • Affects: non_neg_number/0 built-in type, non_empty_list/0 built-in type.
    • Proposed:
      • nonneg_number/0 and nonempty_list/0, or
      • non_neg_number/0 and non_empty_list/0.
    • Notes: "non" is a prefix, that can be used along with hyphen as in non-negative, or without it as in nonnegative. most of the words in English use it without a hyphen, but both, non-empty and non-negative are found. My proposal is to stick to one convention for all prefixes using "non", preferably using the hyphen/underscore to avoid having things like "nonneg_number".
    • References: non-empty and non-negative entries in Wikitionary.

ExUnit

  • DocTest vs doctest The module DocTest is considered two words (the file is named lib/ex_unit/lib/ex_unit/doc_test.ex), but the function is named "doctest/2".
    • Affects: ExUnit.DocTest, ExUnit.DocTest.doctest.
    • Proposed: ExUnit.Doctest and ExUnit.Doctest.doctest.
    • Proposed alternatively: ExUnit.DocTest and ExUnit.DocTest.doc_test.

Mix

Data

  • Deps status:
    • Affects: :divergedonly, :divergedreq, :elixirlock, :invalidapp, :invalidvsn, :lockmismatch, :lockoutdated, :noappfile, :nolock, :nomatchvsn, :nosemver, :scmlock.
    • Proposed: :diverged_only, :diverged_req, :elixir_lock, :invalid_app, :invalid_vsn, :lock_mismatch, :lock_outdated, :no_app_file, :no_lock, :no_match_vsn, :no_sem_ver, :scm_lock.

Tasks

  • loadconfig

    • Affects: mix loadconfig, Mix.Tasks.Loadconfig.
    • Proposed: mix load_config, Mix.Tasks.LoadConfig.
  • loadpaths

    • Affects: mix loadpaths, mix deps.loadpaths, Mix.Tasks.Loadpaths, Mix.Tasks.Deps.Loadpaths.
    • Proposed: mix load_paths, mix deps.load_paths, Mix.Tasks.LoadPaths, Mix.Tasks.Deps.LoadPaths.
    • Precedents: Mix.Projects.load_paths/1, mix load_all, mix load_tasks.

Implemented

The following proposals have already been implemented.

Elixir

Modules and functions

  • char_list
    • Affected:
      • Atom.to_char_list/1, Float.to_char_list/1,2, Integer.to_char_list/1,2, Kernel.to_char_list/1, List.Chars.to_char_list/1, String.to_char_list/1.
    • Proposed and Implemented:
      • Atom.to_charlist/1, Float.to_charlist/1, Integer.to_charlist/1,2, Kernel.to_charlist/1, List.Chars.to_charlist/1, String.to_charlist/1.
    • Precedents: empty_charlist/0 built-in type.
    • Notes: The proposed change has been implemented in v1.3 and hard-deprecated in v1.5. Float.to_char_list/2 was completely hard-depreacted in v1.4 with no replacement in Elixir.

Types

  • char_list
    • Affects: char_list/0 built-in type, :char_lists key and :as_char_lists value in [Inspect.Opts.t/0] type.
    • Proposed: charlist/0, :charlists, :as_charlists.
    • Precedents: empty_charlist/0 built-in type.
    • Notes: The proposed change has been implemented in v1.3 and hard-deprecated in v1.5.