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 Protocol
s 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