Coder Social home page Coder Social logo

elixir_course's Introduction

Учебный курс по языку программирования Эликсир. Часть 1.

Курс рассчитан на программистов, уже владеющих каким-либо языком программирования, и желающим изучить Эликсир. Однако я верю, что он будет полезен и новичкам в программировании.

Курс содержит большое количество упражнений, домашних заданий и курсовой проект.

Курс доступен в формате видео-лекций и live coding на моём ютуб-аккаунте.

Формат электронной книги есть в планах, но пока не реализован.

Поддержать работу над курсом можно на Boosty.

Часть 1. Основы языка.

Урок №1. Первое знакомство с Elixir.

01.01. Решаем задачу FizzBuzz.

01.02. Интерактивная консоль (iex).

Урок №2. История, мотивация.

02.01. Немного истории.

02.02. Важные свойства BEAM.

02.03. Свойства Эликсир.

02.04. Недостатки BEAM.

02.05. Сфера применения.

Урок №3. Типы данных и операции над ними.

03.00. Типы данных.

03.01. Целые числа (Integer).

03.02. Числа с плавающей точкой (Float).

03.03. Булевые значения (Boolean).

03.04. Атомы и кортежи (Atom & Tuple).

03.05. Списки (List).

03.06. Словари (Map).

03.07. Строки и бинарные данные (String & Binary).

03.08. Системные типы (System Types).

03.09. Сложные типы (Complex Types).

Урок №4. Управление потоком выполнения.

04.01. Pattern Matching.

04.02. Pattern Matching для Map.

04.03. Конструкция case, тела функций, охранные выражения.

04.04. Конструкции cond и if.

04.05. Keyword list, синтаксический сахар и макросы.

Урок №5. Устройство списков. Рекурсивные функции с аккумуляторами.

05.01. Иммутабельность.

05.02. Устройство списков.

05.03. Рекурсия.

05.04. Хвостовая рекурсия.

05.05. Рекурсивные функции с аккумуляторами.

05.06. Неограниченная рекурсия (Unbounded recursion).

Урок №6. Функции высшего порядка.

06.01. Map, Filter.

06.02. Reduce (Fold).

06.03. Модуль Enum.

06.04. Конструкторы списков.

06.05. Модуль Stream.

Урок №7. Пользовательские типы данных.

07.01. Создание проекта и моделирование предметной области.

07.02. Использование Map.

07.03. Использование Struct.

07.04. Struct с указанием типов.

07.05. Протокол, практика.

07.06. Протокол, теория.

07.07. Что такое функциональное программирование?

Урок №8. Обработка ошибок.

08.01. Исключения.

08.02. Классы исключений.

08.03. Пользовательские типы исключений.

08.04. Defensive Programming vs Let It Crash.

Урок №9. Композиция функций.

09.01. Задача на композицию функций.

09.02. Решение 1. Вложенные case.

09.03. Решение 2. Каждый case в отдельной функции.

09.04. Решение 3. Использование исключений.

09.05. Решение 4. Монада Result и оператор bind.

09.06. Решение 5. Pipeline.

09.07. Решение 6. do-нотация.

09.08. Что такое монада?

Курсовой проект Work Report.

Часть 2. Многопоточность и OTP.

Находится здесь.

elixir_course's People

Contributors

dx3mod avatar edmtsky avatar goddesseyes avatar hissssst avatar lemurchik avatar pilot93 avatar raventid avatar solar05 avatar v-spassky avatar vkatsuba avatar yuri-sn avatar yzh44yzh avatar zhabinka 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

elixir_course's Issues

lesson_03_06_map text update

Maps are the go-to key/value data structure in Elixir. They have good performance at all sizes.

В целом использование других key-value структур данных в Elixir узкоспецифично и словари покроют почти все ваши потребности при разработке.


Разное поведение при доступе к несуществующему ключу:

iex(5)> m.c
** (KeyError) key :c not found in: %{a: 45, b: 100}

iex(5)> m[:c]
nil

Разное поведение при доступе к несуществующему ключу:

iex(5)> m.c
** (KeyError) key :c not found in: %{a: 45, b: 100}

iex(5)> m[:c]
nil

Разница отражает поведение используемых словарей. Словари с атомами в качестве ключей m.c используются для статичных структур, в которых ключи не изменяются и известны заранее, осюда и появляется ошибка. А m[:d] предполагает более динамичные структуры с изменяемым количеством пар ключ-значение, соответсвенно вызов отсутсвующего ключа нормальная ситуация, а ответ nil полезная информация о состоянии словаря.


TODO: хорошие примеры

Key access

Map.get(my_map, key) # same as [], but you can specify the default value
Map.fetch -> {:ok, val} | :error
Map.fetch! -> throws KeyError

Можно удалить эту заметку, она покрывается дальше


Updating a map

Map is immutable, and so the result of the update is a new map.

Map.put
new_map = %{ old_map | key1 => value1, key2 => value2 }

This creates a new map that is a copy of the old,
but the values associated with the keys on the right of the pipe character are updated.

However, this syntax will not add a new key to a map.
To do this, you have to use the Map.put_new/3 function.

throws KeyError

Эту часть можно удалить, она покрывается, в части, где рассматривается put, put_new. Достаточно в разделе о put упомянуть, что из-за имутабельности создаётся новая структура.

Lesson 03_05_list exersice complition

Вариант описания упражнения по написанию merge

Упражнение

Реализуем операцию merge из алгоритма merge sort -- слить два отсортированых списка в один.

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

Сортировку связных списков трудно сделать эффективно. А операцию merge -- легко.

Начнём с описания желаемой функции

  @spec merge(list(), list()) :: list()
  def merge(list1, list2) do
    merge(list1, list2, [])
  end

Она принимает на вход два списка и отдаёт один. Изначально новый список пуст, мы будем передавать его третьим аргументом при вызове нашей функции.

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

  defp merge(list1, list2, acc) do
    [head1 | tail1] = list1
    [head2 | tail2] = list2

    if head1 < head2 do
      merge(tail1, list2, [head1 | acc])
    else
      merge(list1, tail2, [head2 | acc])
    end
  end

Результатом нашей функции является вызов той же функции, но с изменёнными входными параметрами. Как вы наверняка знаете, это называется рекурсией. Написанный фрагмент является базой нашей рекурсии.

Как только один из списков закончится (станет пустым), наша операция сравнения потеряет смысл. Так как мы каждый раз добавляли элемент в начало аккумулятора, промежуточный список сейчас находится в инвертированном состоянии. Нам требуется этот список развернуть и добавить к нему непустой список.

  defp merge([], list2, acc), do: Enum.reverse(acc) ++ list2
  defp merge(list1, [], acc), do: Enum.reverse(acc) ++ list1

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

Вот как выглядит итоговый модуль:

  @spec merge(list(), list()) :: list()
  def merge(list1, list2) do
    merge(list1, list2, [])
  end

  defp merge([], list2, acc), do: Enum.reverse(acc) ++ list2
  defp merge(list1, [], acc), do: Enum.reverse(acc) ++ list1

  defp merge(list1, list2, acc) do
    [head1 | tail1] = list1
    [head2 | tail2] = list2

    if head1 < head2 do
      merge(tail1, list2, [head1 | acc])
    else
      merge(list1, tail2, [head2 | acc])
    end
  end
end

Проверим работу нашего модуля:

iex(1)> c "lib/list_example.exs"
[ListExample, ListExampleTest]
iex(2)> ListExample.merge([5, 6, 10], [1, 3, 8, 13])
[1, 3, 5, 6, 8, 10, 13]
iex(3)> ListExample.merge([-10, 0, 5], [-5, 0, 5])
[-10, -5, 0, 0, 5, 5]

Добавим тесты:

ExUnit.start()

defmodule ListExampleTest do
  use ExUnit.Case
  import ListExample

  test "merge" do
    assert [1, 2, 3, 4, 5] == merge([1, 3], [2, 4, 5])
    assert [-100, 0, 2, 22, 55, 500, 1000] == merge([2, 22, 500, 1000], [-100, 0, 55])
  end

  test "merge, corner cases" do
    assert [] == merge([], [])
    assert [1, 2, 3] == merge([], [1, 2, 3])
    assert [1, 2, 3] == merge([1, 2, 3], [])
  end
end

и проверим:

elixir lib/list_example.exs
..
Finished in 0.06 seconds (0.06s on load, 0.00s async, 0.00s sync)
2 tests, 0 failures

Randomized with seed 496805

lesson_03_08_system_types text update

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

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

Можно ещё раз напомнить что такое нода:

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


Сравнение значений

вместо:

Сравнение значений

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

можно дополнить:

От намеренной огранниченности в средствах выражения языка появляются необычные возможности в виде сравнения любых переменных, так как система типов конструируется из нескольких основных, для которых задано отношения вида:

Кастомный оператор для `bind`.

В девятом уроке написано следующие:

Увы, в Эликсире нельзя сделать как в Хаскеле, в префиксном виде:
f = validate_incoming_data >>= validate_cat >>= validate_address >>= validate_books >>= create_order
f test_data

Но в Эликсире же можно сделать кастомный оператор(если вы это, конечно, имели в виду :) ). Вариация не такая большая, как в Хаскеле, но всё же. Вот и я прикинул как это было бы:

defprotocol Monad do
  @spec Monad.t() ~> (any() -> Monad.t()) :: Monad.t()
  def x ~> fy
end

defmodule Maybe do
  @type t(a) :: %Maybe{container: :error | {:ok, a}}
  @type t :: Maybe.t(any())

  @type some(a) :: %Maybe{container: {:ok, a}}
  @type non() :: %Maybe{container: :error}

  defstruct [:container]

  @spec some(t) :: some(t) when t: any
  def some(value), do: %Maybe{container: {:ok, value}}

  @spec non() :: Maybe.non()
  def non(), do: %Maybe{container: :error}
end

defimpl Monad, for: Maybe do
  def x ~> fy do
    case x do
      %Maybe{container: {:ok, value}} -> fy.(value)
      %Maybe{container: :error} -> :error
    end
  end
end

И использовать это можно след образом:

@spec foo(t) :: Maybe.t(t) when t: integer
def foo(x) when x > 7, do: Maybe.some(x)
def foo(_), do: Maybe.non()

@spec square(t) :: Maybe.some(t) when t: number
def square(x), do: Maybe.some(x ** 2)
foo(6) ~> (&square/1) ~> (&square/1) # => :error
foo(8) ~> (&square/1) ~> (&square/1) # => {:ok, 4096}

И, вспоминая пример из текста, можно его повторить примерно c оговорками:

f = fn x -> foo(x) ~> (&square/1) end
f.(data) 

Стоит ли делать ремарку? И если да, то может просто указать в качестве ссылки этот issue? :)

lesson_07_05_typed_struct learning experience

После вызова mix dialyzer

Ошибок с типом 10 штук, а не только с Broom.t

mix dialyzer
Finding suitable PLTs
Checking PLT...
[:compiler, :elixir, :event, :kernel, :logger, :stdlib]
PLT is up to date!
No :ignore_warnings opt specified in mix.exs and default does not exist.

Starting Dialyzer
[
  check_plt: false,
  init_plt: '/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/dialyxir_erlang-26.0.2_elixir-1.14.2_deps-dev.plt',
  files: ['/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/lib/event/ebin/Elixir.Model.Map.Participant.beam',
   '/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/lib/event/ebin/Elixir.Model.TypedStruct.Address.beam',
   '/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/lib/event/ebin/Elixir.Model.Tuple.Participant.beam',
   '/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/lib/event/consolidated/Elixir.Model.CalendarItem.beam',
   '/home/dimawork/Programs/elixir/zhloba_course/elixir_course/lesson_07/event/_build/dev/lib/event/ebin/Elixir.Model.Calendar.beam',
   ...],
  warnings: [:unknown]
]
Total errors: 10, Skipped: 0, Unnecessary Skips: 0
done in 0m1.6s
lib/model/calendar.ex:6:unknown_type
Unknown type: CalendarItem.t/0.
________________________________________________________________________________
lib/model/calendar.ex:17:unknown_type
Unknown type: CalendarItem.t/0.
________________________________________________________________________________
lib/model/event_record.ex:73:unknown_type
Unknown type: Partcipant.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:7:unknown_type
Unknown type: Location.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:8:unknown_type
Unknown type: Participant.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:9:unknown_type
Unknown type: Topic.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:16:unknown_type
Unknown type: CalendarItem.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:21:unknown_type
Unknown type: CalendarItem.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:46:unknown_type
Unknown type: Address.t/0.
________________________________________________________________________________
lib/model/event_typed_struct.ex:47:unknown_type
Unknown type: Broom.t/0.
________________________________________________________________________________
done (warnings were emitted)
Halting VM with exit status 2

lesson_03_01_int learning experience

  1. 114 строка, возможно стоит добавить, что код нужно добавить перед телом основной функции.
  2. 141 строка также стоит уточнить что этот код должен быть до тела основной функции

В целом в этом уроке можно уделить пару предложений важности порядка объявления функций. А после проверки работоспособности тестов предложить поменять местами клозы функций и снова проверить результаты тестов.

lesson_09_05_bind learning experience

(_, {:error, _} = acc) -> acc

Кажется, что в это строчке возращается кортеж с ошибкой, а значит и переменной больше подходит имя e, а не acc. По крайней мере меня, очень сильно запутало, начал сомневаться, что понимаю функцию Enum.reduce

Битая ссылка 09-08

В качестве дополнительного материала по теме можно посмотреть [реализацию функции `curry`](https://www.toptal.com/developers/hastebin/enijabupup.kotlin) для функций любой арности.

К сожалению, страница не доступна.

Lesson_01_01_fizzbuzz learning experience

  1. В первом уроке на первом шаге, было бы здорово сразу подсказать название файла для модуля FizzBuzz01: fizz_buzz_01.exs.
  2. iex запускается в командной строке командой iex, об этом стоит упомянуть в первом шаге, при сборке модуля
  3. на 3-м шаге первого урока. Хорошо бы подсказать название файла для тестов в начале шага fizz_buzz_01.exs.

Ещё возможны трудности с тем что iex запускается из каталога родительского по отношению к lib, возможно стоит добавить описание где, что находится, чтобы учащиеся не тратили время на ненужные для понимания языка вещи.

lesson_05_03_recursion text update

TODO: Еще неплохо было бы придумать пример опосредованой рекурсии, через 2 функции.

Пример 5

Реализуем функции проверяющие натуральное число на чётность используя определения:

  • число является чётным, если оно на 1 больше нечётного числа
  • число является нечётным, если оно на 1 больше чётного числа
  • 0 -- чётное число
def is_even(0), do: true
def is_even(n), do: is_odd(n-1)
def is_odd(0), do: false
def is_odd(n), do: is_even(n-1)

TODO: поискать больше инфы по этой теме. В UA не особо хорошо описано.

Можно упомянуть про древовидную рекурсию с поиском числа фиббоначи

lesson_03_07_string text update

Кажется можно смело удалять текст на английском начиная с этой строки


## Задача align words:

Можно немного модифицировать:

Задача align words:

В файле lib/string_example.exs рассматривается задача выравнивания слов по центру.

iex(1)> c "lib/string_example.exs"
[StringExample, StringExampleTest]
iex(2)> words = ~w'cat zebra elephant'
["cat", "zebra", "elephant"]
iex(3)> Enum.map(words, &String.length/1)
[3, 5, 8]
iex(4)> StringExample.align(words)
["  cat   ", " zebra  ", "elephant"]

Запускаем тесты:

elixir lib/string_example.exs
...
Finished in 0.05 seconds (0.05s on load, 0.00s async, 0.00s sync)
3 tests, 0 failures

Randomized with seed 312660

Модифицируйте модуль StringExample и тесты к нему так чтобы слова выравнивались по правому краю.

lesson_07_07_protocol text update

Теперь реализуем этот протокол для наших Event разного типа:

Напоминание разделить описание реализаций для Map, Struct, TypedStruct и уделить несколько слов реализации протокола для Map.

lesson_03_09_complex_types text update part 1

IO List

Recursive data structure, consists of:

  • byte (integer in range 0-255)
  • String.t (binary)
  • IO List

Useful for incrementally building output that will be forwarded to an IO device (socket or file).

Не эффективно:

IO List

IO List -- пример сложного типа данных, который облегчит работу разработчика и виртуальной машины.

Он состоит будет состоять из типов:

  • byte (integer in range 0-255)
  • String.t (binary)

И рекурсивно состоять из самого себя:

  • IO List

Сначала рассмотрим пример по построению html странички, используя только строковый тип:


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.