[erlang-questions] Lazy lists
Richard O'Keefe
ok@REDACTED
Tue Dec 8 01:37:02 CET 2009
I never really expected to mention coinductive data types in the
Erlang mailing list...
To support "lazy (infinite) list"s, you need a data type C(E)
[C = container E = element] and a function
view :: C(E) -> 0 + (E x C(E))
which we might express in Erlang as
view(CE) ---> []
or view(CE) ---> [E | CE']
The representation from the Erlang documentation is
C(E) = fun(() -> [] | [E | C(E)])
view(CE) = CE()
The other representation is
C(E) = [] | [E | fun(() -> C(E))]
view(CE) = case CE of [] -> [] ; [X|F] -> [X|F()] end
which has the down-side of evaluating one more element than you
actually need.
With the first representation,
map(F, CE) ->
fun (()) ->
case CE()
of [] -> []
; [X|Xs] -> [F(X) | map(F, Xs)]
end
end.
compared with
map(F, L) ->
case L
of [] -> []
; [X|Xs] -> [F(X) | map(F, Xs)]
end.
for an ordinary list.
filter(P, CE) ->
fun (()) ->
case CE()
of [] -> []
; [X|Xs] -> case P(X)
of true -> [X | filter(P, Xs)]
; false -> (filter(P, Xs))()
end
end
end.
compared with
filter(P, L) ->
case L
of [] ->
; [X|Xs] -> case P(X)
of true -> [X | filter(P, Xs)]
; false -> filter(P, Xs)
end
end.
for an ordinary list.
foldl(F, A, CE) ->
case CE()
of [] -> A
; [X|Xs] -> foldl(F, F(A,X), Xs)
end.
compared with
foldl(F, A, L) ->
case L
of [] -> []
; [X|Xs] -> foldl(F, F(A,X), Xs)
end.
for an ordinary list.
Step 0: rewrite pattern matching to use case.
Step 1: replace case L of [] -> E0 ; [X|Xs] -> E1 end
with case view(L) of [] -> E0 ; [X|Xs] -> E1 end
Step 2: if you have g(...), known to return a lazy list,
in a context where a non-lazy result is required,
replace it by view(g(...)).
Step 3: if the result is a list,
replace f(...) -> E.
with f(...) -> fun () -> E end.
More information about the erlang-questions
mailing list