Skip to content

Instantly share code, notes, and snippets.

@ritalin
Created September 2, 2014 14:27
Show Gist options
  • Save ritalin/67ccbbf36f7956ef5801 to your computer and use it in GitHub Desktop.
Save ritalin/67ccbbf36f7956ef5801 to your computer and use it in GitHub Desktop.
型クラスっぽく見える何か by Elixir
defmodule Option do
defmodule None, do: defstruct []
defmodule Some, do: defstruct [:value]
def none(), do: %None{}
def some(x), do: %Some{ value: x }
end
defprotocol FunctorSpec do
@type t :: term
@type result :: term
@type mapper :: (any -> term)
@spec fmap(t, mapper) :: result
def fmap(t, mapper)
end
defimpl FunctorSpec, for: Option.None do
def fmap(%Option.None{} = t, _mapper), do: t
end
defimpl FunctorSpec, for: Option.Some do
def fmap(%Option.Some{ value: x }, mapper) when is_function(mapper, 1) do
%Option.Some{ value: mapper.(x) }
end
end
defimpl FunctorSpec, for: List do
def fmap(list, mapper) when is_function(mapper, 1) do
list |> Enum.map mapper
end
end
defimpl FunctorSpec, for: Function do
def fmap(fun, mapper) when is_function(fun, 1) and is_function(mapper, 1) do
fn x -> apply mapper, [apply(fun, [x])] end
end
end
defprotocol ApplicativeSpec do
@type a :: term
@type b :: term
@type tc(term) :: term
@type mapper :: (a -> b)
@spec fmap(a, mapper) :: tc(b)
def fmap(x, mapper)
@spec bind(tc(a), tc(mapper)) :: tc(b)
def bind(tc, mapper)
end
defimpl ApplicativeSpec, for: Option.None do
def fmap(%Option.None{} = tc, _mapper) do
tc
end
def bind(tc, _ap) do
tc
end
end
defimpl ApplicativeSpec, for: Option.Some do
def fmap(%Option.Some{} = tc, mapper) do
tc |> bind Option.some mapper
end
def bind(%Option.Some{ value: x }, %Option.Some{ value: mapper }) when is_function(mapper, 1) do
apply(mapper, [x]) |> Option.some
end
end
defimpl ApplicativeSpec, for: List do
def fmap(list, mapper) when is_list(list) do
list |> bind [mapper]
end
def bind(list, mappers) when is_list(list) and is_list(mappers) do
mappers |> Enum.flat_map fn mapper -> list |> Enum.map mapper end
end
end
defprotocol MonadSpec do
def bind(tc, fun)
end
defimpl MonadSpec, for: Option.None do
def bind(%Option.None{} = tc, _fun) do
tc
end
end
defimpl MonadSpec, for: Option.Some do
def bind(%Option.Some{ value: x}, fun) when is_function(fun, 1) do
apply(fun, [x])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment