Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Enumeration

enumeration.livemd

Enumeration

Section

General gist: transform for loops into recursion ; need to be explicit on the state, but with default values that’s probably rather nice. Below: assuming that we have recursion, here is how we can do a for loop (kind off, one style among many possible ; C-style and Python-style would be useful (conceptually) to implement).

defmodule Utils do
  def loop(ante, next, post) do
    state = ante.()
    {status, state} = next.(state)

    case status do
      :stop ->
        post.(state)

      :continue ->
        loop(fn -> state end, next, post)
    end
  end
end
import Utils
# A reduction scheme

list = [0, 1, 2, 3, 4]

ante = fn ->
  {list, 0}
end

next = fn {list, acc} ->
  case list do
    [] ->
      {:stop, {list, acc}}

    [head | tail] ->
      {:continue, {tail, acc + head}}
  end
end

post = fn {_, acc} -> acc end

loop(ante, next, post)
# A filter
list = [0, 1, 2, 3, 4]
ante = fn -> {list, []} end

next = fn {list1, list2} ->
  if list1 == [] do
    {:stop, {list1, list2}}
  else
    [elt | list1] = list1

    if rem(elt, 2) == 0 do
      {:continue, {list1, list2 ++ [elt]}}
    else
      {:continue, {list1, list2}}
    end
  end
end

post = fn {_l1, l2} -> l2 end

loop(ante, next, post)
# A map
list = [0, 1, 2, 3, 4]
ante = fn -> {list, []} end

next = fn {list1, list2} ->
  if list1 == [] do
    {:stop, {list1, list2}}
  else
    [elt | list1] = list1
    {:continue, {list1, list2 ++ [elt * 7]}}
  end
end

post = fn {_l1, l2} -> l2 end

loop(ante, next, post)

Python-Style Iteration ??? Is it even possible without boundary-effects and what would that mean?

… since all data is immutable and variable assignment in a function is not reflected outside of the function. We would at the very least need some explicit “state” to have an effect on the outside data AND return that state at the end … OUCH. The best we could do is to provide a next “function” that returns the initial elt and raises a StopIteration when it’s over? Mmm the only way I think we can do that without explicit state is with a process, right? So we would have the concept of iterator (process). But then we still have to manage the block effect explicitely (even if a do keywork could be used to avoid the appearance of a function call?). And we would need Protocols too right? Mmmm all this is very sketchy so far.

defmodule StopIteration do
  defexception message: ""
end
defprotocol Iterator do
  def next()
end

For Loops

TODO: Enum, Stream: filter, map, etc.

for elt <- [1, 2, 3] do
  IO.puts(elt)
end
for elt <- 0..10 do
  IO.puts(elt)
end
for elt <- 0..10 do
  elt * elt
end
for elt <- 0..10, elt < 5 do
  elt * elt
end
# here, pattern matching would be applicable too.
for elt <- 0..10,
    s <- [0, 1],
    2 < elt,
    # as long as the value if not nil or false (?) assignment is ok?
    two = 2,
    elt < 5 do
  elt * elt * s * two
end

Sandbox

f = fn x, options ->
  IO.puts(x)
  inspect(options) |> IO.puts()
end
f.(42, a: 1, b: 2, c: 42)
# ouch, tuples do not implement `String.Chars`
IO.puts({1, 2})
inspect({1, 2})
f.(1,
  do:
    (
      b = 7
      a = 2
      a + b + 1
    )
)
defmodule Utils do
  def f(options \\ []) do
    options |> inspect |> IO.puts()
  end
end
import Utils
f do
  a = 1
  b = 2
  a + b + 1
end