On wikipedia it says that using call/cc you can implement the amb operator for nondeterministic choice, and my question is how would you implement the amb operator in a language in which the only support for continuations is to write in continuation passing style, like in erlang?
1 Answers
If you can encode the constraints for what constitutes a successful solution or choice as guards, list comprehensions can be used to generate solutions. For example, the list comprehension documentation shows an example of solving Pythagorean triples, which is a problem frequently solved using amb
(see for example exercise 4.35 of SICP, 2nd edition). Here's the more efficient solution, pyth1/1
, shown on the list comprehensions page:
pyth1(N) ->
[ {A,B,C} ||
A <- lists:seq(1,N-2),
B <- lists:seq(A+1,N-1),
C <- lists:seq(B+1,N),
A+B+C =< N,
A*A+B*B == C*C
].
One important aspect of amb
is efficiently searching the solution space, which is done here by generating possible values for A
, B
, and C
with lists:seq/2
and then constraining and testing those values with guards. Note that the page also shows a less efficient solution named pyth/1
where A
, B
, and C
are all generated identically using lists:seq(1,N)
; that approach generates all permutations but is slower than pyth1/1
(for example, on my machine, pyth(50)
is 5-6x slower than pyth1(50)
).
If your constraints can't be expressed as guards, you can use pattern matching and try/catch to deal with failing solutions. For example, here's the same algorithm in pyth/1
rewritten as regular functions triples/1
and the recursive triples/5
:
-module(pyth).
-export([triples/1]).
triples(N) ->
triples(1,1,1,N,[]).
triples(N,N,N,N,Acc) ->
lists:reverse(Acc);
triples(N,N,C,N,Acc) ->
triples(1,1,C+1,N,Acc);
triples(N,B,C,N,Acc) ->
triples(1,B+1,C,N,Acc);
triples(A,B,C,N,Acc) ->
NewAcc = try
true = A+B+C =< N,
true = A*A+B*B == C*C,
[{A,B,C}|Acc]
catch
error:{badmatch,false} ->
Acc
end,
triples(A+1,B,C,N,NewAcc).
We're using pattern matching for two purposes:
- In the function heads, to control values of
A
,B
andC
with respect toN
and to know when we're finished - In the body of the final clause of
triples/5
, to assert that conditionsA+B+C =< N
andA*A+B*B == C*C
matchtrue
If both conditions match true
in the final clause of triples/5
, we insert the solution into our accumulator list, but if either fails to match, we catch the badmatch
error and keep the original accumulator value.
Calling triples/1
yields the same result as the list comprehension approaches used in pyth/1
and pyth1/1
, but it's also half the speed of pyth/1
. Even so, with this approach any constraint could be encoded as a normal function and tested for success within the try/catch expression.

- 19,847
- 3
- 31
- 46