Упасть - значит поднять

Эта статья посвящена типичным ошибкам новичков в Elixir. Под эту категорию иногда попадают даже знатоки Эликсира. Исходя из своего опыта, я показываю, что при кодировании следует соблюдать осторожность. Это не означает, что вы попадаете в эту категорию. Я просто говорю, что есть ямы, и вы в них не упадете. Не волнуйся, я шучу. Давайте прыгнем в виртуальные ловушки…

1. Функции с оператором | ›и параметрами

Это наиболее распространенная ошибка, и нужно быть более осторожным при настройке функций с параметрами, что приводит к необычным результатам. Давайте проверим следующий пример.

String.graphemes "hello" |> length 

Когда вы выполните указанную выше строку, вы получите ArgumentError. Это потому, что приведенные выше строки сгруппированы следующим образом

quote(do: String.graphemes "hello" |> length) |> Macro.to_string
"String.graphemes(\"hello\" |> length)"

Итак, вы можете использовать quote и Macro.to_string, чтобы увидеть, как ваши строки кода сгруппированы вместе. Мы думаем, что в направлении вывода String.graphemes передается как вход в функцию length. Но он делает в другом направлении, принимая двоичный "hello" в качестве входных данных для функции length, что дает вам ошибку, поскольку ожидается list.

Чтобы избежать этого, мы должны использовать круглые скобки для заключения параметров внутри них для безопасной оценки кода.

String.graphemes("hello") |> length
5

2. Объединение списков / неправильные списки

В Elixir есть оператор ++ для добавления элементов в список. В основном он используется для объединения двух списков, но мы всегда забываем обернуть элементы внутри []

value = 99
list = [1,2,3,4,5]
list ++ value

Мы думаем, что результатом list ++ value будет [1,2,3,4,5,99], но в целом это будет [1,2,3,4,5|99]. Это неправильный список. Вы не можете использовать length функцию заново. В правильном списке, когда вы перебираете список, хвост будет [] пустым списком. С неправильным списком дело обстоит иначе.

Итак, чтобы преодолеть это, мы должны заключить значение в список как
list ++ [value]

Подробнее про неправильный список ЗДЕСЬ.

3. Строки с чарлистами

В Эликсире "hello" не равно 'hello'. Когда вы используете "", это считается binary в наших терминах строкой, а когда вы используете '', это список символов. Это самая распространенная ловушка для новичков. Однажды, если вы придете к ясному представлению о binary и charlists, больше нечего бояться.

iex> is_binary 'hello'
false
iex> is_list 'hello'
true
iex> is_binary "hello"
true
iex> length 'hello'
5
iex> length "hello"
** (ArgumentError) argument error
    :erlang.length("hello")

4. Анонимные вызовы функций и трубопроводы

Их также называют лямбда-функциями, которые очень удобны при анализе данных . Вызов этих функций мало чем отличается от обычного вызова.

square = & &1 * &1

мы можем написать то же самое, что и

square = fn(x)-> x * x end

Когда вы попытаетесь получить к ним доступ как square(5), вы получите сообщение об ошибке компиляции undefined function square/1. Вы предполагаете вызывать лямбда-функции как square.(3). Это просто отличается от обычных функций, определенных внутри модуля.

Это очень частая ошибка новичков в эликсире. С такими вещами нужно быть очень ясным. Однако эту ошибку можно преодолеть, используя _ в имени функции, чтобы отличать их от обычных функций, таких как _square. Итак, глядя на имя, вы узнаете его как лямбда-функцию.

Вы можете использовать свой собственный действительный знак, чтобы отличать ваши лямбда-функции от обычных функций.

Распределение анонимных функций по конвейеру также отличается. Вы должны сделать следующее

5
|> square.()
or
5
|> (&(&1*&1)).()

5. Логическое значение False, которое не является ложным.

В Эликсире False нет false. В Эликсире только false и nil считаются false. Остальное все оценивается в true. В C Programming даже 0 считаются false. Но в Эликсире их нет. В Эликсире все, что начинается с верхнего регистра, также является атомом. Итак, False оценивается как true и удивляет нас, когда мы думаем, что это ложь.

Таким образом, очень часто можно ошибиться с False. Всегда избегайте использования заглавных букв до тех пор, пока это не обязательно.

iex(1)> logic=False
False
iex(2)> if logic do
...(2)> " I am true"
...(2)> else
...(2)> "I am false"
...(2)> end
" I am true"
iex(3)>

6. Неисправности охранной оговорки

В elixir мы можем использовать защитные предложения в определениях функций, используя when. Однако, если в предложении защиты есть ошибки, они не просочатся. Это просто не соответствует оговорке о защите. Он не вызывает никаких исключений или ошибок времени выполнения и ищет другое подходящее определение, считая предыдущую защиту неудачной. Давайте посмотрим на следующий пример

iex> length(15)
** (ArgumentError) argument error
    :erlang.length(15)
iex> case 15 do
...>   x when length(x) -> "Never match"
...>   x -> "value:  #{x}"
...> end
"value: 15"

В приведенных выше строках кода при попытке найти length целого числа возникает ошибка. Но когда вы использовали тот же оператор внутри охранников, он просто пропустил это определение, учитывая отказ охранника. Это очень часто бывает у новичков, которые ошибаются в защитных оговорках.

7. Порядок ключевых слов в вызове функций имеет значение.

В Elixir действительно имеет значение порядок передачи ключевых слов в качестве параметров функции. Рассмотрим следующие строки кода.

defmodule User do
 def user name: name,age: age do
 "name: #{name}-- age: #{age}"
 end
end

Вышеупомянутые строки кода определяют модуль User с функцией user/1. При определении функции мы попросили, чтобы за name сначала следовало age. Давайте проверим различные способы вызова указанной выше функции.

iex> User.user age: 12,name: "hello"
** (FunctionClauseError) no function clause matching in User.user/1
    iex:2: User.user([age: 12, name: "hello"])

Когда вы меняете порядок параметров, вы получаете ошибку отсутствия функции FunctionClauseError. Это потому, что функции Elixir определены вместе с arity. Итак, шаблон здесь не совпадает.

iex> User.user name: "hello",age: 12
"name: hello-- age: 12"

Итак,
def user name: name,age: age
не равно
def user age: age, name: name

Решение

Если вы хотите, чтобы ключевые слова могли передаваться в любом порядке, вы должны принять любой список в заголовке функции, а затем вместо этого использовать Keyword.get внутри функции:

defmodule User do
   def user(options) do
     name = Keyword.get(options, :user)
     age = Keyword.get(options, :age)
     "name: #{name}-- age: #{age}"
   end
end

Это также позволяет вам указать полезные значения по умолчанию для ключевых слов, которые не включаются при вызове функции.

Это совет от Wiebe-Marten / Qqwy

8. Получение значений из карты

В Elixir, если вы определяете карту как map = %{name: "hello"}, вы можете получить доступ к карте для извлечения name двумя способами.

Один использует период ., например map.name. Новички в Эликсире всегда будут пытаться получить доступ к карте с периодом .. Это просто и опасно, потому что при попытке извлечь key, который не определен в карте, возникает ошибка. Посмотрите на следующие строки кода.

iex(1)> map = %{name: "hello"}
%{name: "hello"}
iex(2)> map.name
"hello"
iex(3)> map.age
** (KeyError) key :age not found in: %{name: "hello"}
    
iex(3)> map["name"]
nil
iex(4)> map[:name] 
"hello"
iex(5)> map[:age] 
nil

В приведенных выше строках кода мы определяем map с атомом name в качестве ключа. В строке выполнения iex(3) мы пытаемся извлечь значение age, используя период ., но это вызвало ошибку, потому что ключа нет.

В том же стиле, когда вы пытаетесь получить доступ к карте с помощью оператора доступа [], он дает вам значение nil, когда ключ отсутствует, вместо того, чтобы вызывать ошибку. Таким образом, это очень удобно, когда вы обращаетесь к карте с помощью [:key] или ["key"]. Чтобы использовать как map["name"], ключи в карте должны быть binary следующим образом

map = %{"name" => "hello"}

Вот почему, когда вы пытаетесь использовать key как двоичный, например map["name"], он просто возвращает nil, потому что наш map определен с ключом как atom. Итак, если вы попробуете с map[:name], вы получите значение name.

Примечание. Когда вы определяете карту с ключами как двоичную, вы не можете получить к ним доступ с . точкой. Он доступен только тогда, когда ключи являются атомами.

9. ограничение соответствия шаблонов

Сопоставление с образцом также очень полезно и мощно. Как бы то ни было, вы не можете вызывать функции в левой части сопоставления с шаблоном. Посмотрите на следующие строки кода.

iex(2)> 3 = length [1,2,3]
3
iex(3)> length [1,2,3] = 3
** (MatchError) no match of right hand side value: 3

В приведенных выше строках кода, когда я пытаюсь вызвать функцию с левой стороны для соответствия правой стороне, возникает ошибка.

То же самое и для типов данных карты.

Когда вы пробуете %{}=%{name: "hello"}, все работает нормально. Но когда вы меняете стороны, например %{name: "hello"}=%{}, это дает вам ошибку. Попробуй и увидишь

iex(3)> %{}=%{name: "hello"}
%{name: "hello"}
iex(4)> %{name: "hello"}=%{}
** (MatchError) no match of right hand side value: %{}

%{name: "hello"}=%{name: "hello",age: 23} действительно.

%{name: "hello",age: 23}=%{name: "hello"} недействительно.

Таким образом, при сопоставлении с образцом очень часто можно не понять приведенные выше термины.

10 защитных предложений с использованием &&, ||, ! недействительны

Мы можем проверить несколько условий в оговорках о защите. В Эликсире нельзя использовать ни &&, ни||, ни !. Но переходя к оговоркам об охране, это немного другое. Вы должны использовать and, or, not для &&, or и ! соответственно. Взгляните на следующий пример.

iex(13)> x=25
25
iex(14)> case x do 
...(14)> y when y>12 && y<30 -> y
...(14)> y -> "nomatch"
...(14)> end
** (CompileError) iex:15: invalid expression in guard
    (elixir) expanding macro: Kernel.&&/2
             iex:15: (file)

В приведенных выше строках кода мы пытаемся проверить несколько условий в предложении защиты с помощью кода when y>12 && y<30. Несмотря на то, что они выглядят одинаково, они не допускаются в оговорках. Если вы запустите те же строки кода, изменив && на and, он будет работать нормально. Давайте проверим рабочий пример.

iex(14)> case x do               
...(14)> y when y>12 and y<30 -> y
...(14)> y -> "nomatch"           
...(14)> end                      
25

Таким образом, есть шанс сделать ошибку, используя &&, or, ! в условиях защитного предложения, поскольку они отлично работают при обычных оценках условий. Посмотрите следующий пример.

iex(15)> x=25
iex(16)> if x>12 && x<30 do x else :invalid end
25
iex(17) x=1
iex(18)> if x>12 && x<30 do x else :invalid end
:invalid

Вы заметили, что&& работал в обычных условиях, но не разрешался в условиях защитной оговорки.

Если вы найдете какой-либо другой pitfall в коде эликсира, о котором я не упоминал, поделитесь, пожалуйста, и спасите других от попадания в него.

Люблю слышать твои вещи. ❤ ❤ ^ - ^

Если вы сочтете это полезным, порекомендуйте его, чтобы другие могли извлечь из этого пользу!
Поделиться - это забота…

Удачного кодирования! Всегда улыбайся :)