Bootstrap compiler (reader, analyzer, transformer, compiler, Mix plugin), core protocols (16 protocols for Map/List/Tuple/BitString), PersistentVector (bit-partitioned trie), domain tools (clojurify/elixirify), BEAM concurrency (receive, spawn, GenServer), control flow & macros (threading, try/catch, destructuring, defmacro with quasiquote/auto-gensym), and Malli schema adapter (m/=> specs, auto @type, recursive schemas, cross-references). 537 compiler tests + 55 Malli unit tests + 15 integration tests = 607 total. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
2.9 KiB
Elixir
113 lines
2.9 KiB
Elixir
defmodule CljElixir.Equality do
|
|
@moduledoc """
|
|
Cross-type equality for CljElixir.
|
|
|
|
Handles the case where `(= [1 2 3] '(1 2 3))` should return true —
|
|
PersistentVector and list are both sequential types with the same elements.
|
|
"""
|
|
|
|
def equiv(a, b) when a === b, do: true
|
|
|
|
def equiv(%CljElixir.PersistentVector{} = a, %CljElixir.PersistentVector{} = b) do
|
|
a.cnt == b.cnt and CljElixir.PersistentVector.to_list(a) == CljElixir.PersistentVector.to_list(b)
|
|
end
|
|
|
|
def equiv(%CljElixir.PersistentVector{} = a, b) when is_list(b) do
|
|
CljElixir.PersistentVector.to_list(a) == b
|
|
end
|
|
|
|
def equiv(a, %CljElixir.PersistentVector{} = b) when is_list(a) do
|
|
a == CljElixir.PersistentVector.to_list(b)
|
|
end
|
|
|
|
def equiv(%CljElixir.SubVector{} = a, b) do
|
|
CljElixir.SubVector.sv_to_list(a) |> equiv_list(b)
|
|
end
|
|
|
|
def equiv(a, %CljElixir.SubVector{} = b) do
|
|
equiv_list(CljElixir.SubVector.sv_to_list(b), a)
|
|
end
|
|
|
|
def equiv(a, b), do: a == b
|
|
|
|
defp equiv_list(list, other) when is_list(other), do: list == other
|
|
|
|
defp equiv_list(list, %CljElixir.PersistentVector{} = pv) do
|
|
list == CljElixir.PersistentVector.to_list(pv)
|
|
end
|
|
|
|
defp equiv_list(_, _), do: false
|
|
end
|
|
|
|
defimpl Enumerable, for: CljElixir.PersistentVector do
|
|
def count(pv), do: {:ok, pv.cnt}
|
|
|
|
def member?(_pv, _value), do: {:error, __MODULE__}
|
|
|
|
def reduce(_pv, {:halt, acc}, _fun), do: {:halted, acc}
|
|
def reduce(pv, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(pv, &1, fun)}
|
|
|
|
def reduce(pv, {:cont, acc}, fun) do
|
|
list = CljElixir.PersistentVector.to_list(pv)
|
|
Enumerable.List.reduce(list, {:cont, acc}, fun)
|
|
end
|
|
|
|
def slice(pv) do
|
|
size = pv.cnt
|
|
{:ok, size, &slice_fun(pv, &1, &2, &3)}
|
|
end
|
|
|
|
defp slice_fun(pv, start, length, step) do
|
|
start..(start + (length - 1) * step)//step
|
|
|> Enum.map(fn i -> CljElixir.PersistentVector.pv_nth(pv, i) end)
|
|
end
|
|
end
|
|
|
|
defimpl Collectable, for: CljElixir.PersistentVector do
|
|
def into(pv) do
|
|
collector_fun = fn
|
|
acc, {:cont, elem} -> CljElixir.PersistentVector.pv_conj(acc, elem)
|
|
acc, :done -> acc
|
|
_acc, :halt -> :ok
|
|
end
|
|
|
|
{pv, collector_fun}
|
|
end
|
|
end
|
|
|
|
defimpl Enumerable, for: Tuple do
|
|
def count(t), do: {:ok, tuple_size(t)}
|
|
|
|
def member?(_t, _value), do: {:error, __MODULE__}
|
|
|
|
def reduce(_t, {:halt, acc}, _fun), do: {:halted, acc}
|
|
def reduce(t, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(t, &1, fun)}
|
|
|
|
def reduce(t, {:cont, acc}, fun) do
|
|
list = Tuple.to_list(t)
|
|
Enumerable.List.reduce(list, {:cont, acc}, fun)
|
|
end
|
|
|
|
def slice(t) do
|
|
size = tuple_size(t)
|
|
{:ok, size, &slice_fun(t, &1, &2, &3)}
|
|
end
|
|
|
|
defp slice_fun(t, start, length, step) do
|
|
start..(start + (length - 1) * step)//step
|
|
|> Enum.map(fn i -> elem(t, i) end)
|
|
end
|
|
end
|
|
|
|
defimpl Collectable, for: Tuple do
|
|
def into(t) do
|
|
collector_fun = fn
|
|
acc, {:cont, elem} -> :erlang.append_element(acc, elem)
|
|
acc, :done -> acc
|
|
_acc, :halt -> :ok
|
|
end
|
|
|
|
{t, collector_fun}
|
|
end
|
|
end
|