yzh44yzh/elixir_course

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

Closed this issue · 1 comments

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

Увы, в Эликсире нельзя сделать как в Хаскеле, в префиксном виде:
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? :)

Спасибо за комментарий. Да, многие элементы ФП, которых нет из коробки, можно реализовать. Или найти готовые библиотеки, в которых они реализованы.

Пожалуй, есть смысл указать в курсе такие библиотеки.