http://www.call-with-current-continuation.org/articles/forth...
% print even numbers between 1 and 10
main :- count(1).
count(11).
count(N) :- N =< 10 | even(N), N2 is N + 1, count(N2).
even(N) :- M is N \\ 2, even(M, N).
even(0, N) :- writeln(N).
even(_, _) :- otherwise | true.
[0] http://www.call-with-current-continuation.org/strand/MANUALIn this snippet:
count(N) :- N =< 10 | even(N), N2 is N + 1, count(N2).
count(N) is defined defined as a process that takes an argument named N, and if N is less than or equal to 10, the process changes state to a random process on the right side, and is also forked to become the other two processes.Where it gets weird, is that the resulting three processes have data dependencies between them, so if the third process were executed first, count(N2), the dependency on N2 wouldn't be satisfied, and so that process would suspend, and a new process be chosen for execution.
It's easy to look at this line of code and think that the three expressions will execute in the order they're written, but any interleaving of them is possible, with that sort of non-determinism being built into the language by design.
I'm currently reading the Strand book, and it more or less describes the language as being Prolog, but without unification and backtracking: instead treating dataflow as being the basis for the computational model.
I'm actually sharing this in the first place because I managed to acquire a copy of "Strand: New Concepts in Parallel Programming" [2] yesterday, and it includes a case study about the Erlang -> Strand compiler, so I've been having fun trying to piece together the lineage.
[0] https://erlang.org/download/armstrong_thesis_2003.pdf
[1] http://erlang.org/pipermail/erlang-questions/2007-September/...
[2] https://www.amazon.com/Strand-New-Concepts-Parallel-Programm...
It lacks Strand's distributed features but includes prolog's unification.
SISAL was probably a better bet as a dataflow-ish language, but I don't remember anyone else looking at it, despite Manchester being "just" down the road.
Whilst concurrency and parallelism are a bit of a nightmare in imperative languages (e.g. multithreading), they turn out to be trivial in such "declarative" languages; in fact the difficulty is usually in reducing parallelism, to keep down the context-switching overheads.
Traditional logic programming languages like Prolog work in a single-threaded depth-first manner, i.e. to execute something like `main` we look at its definition, which might have multiple "clauses" depending on the input data (a bit like doing a `switch` or `cond` to figure out what to return); Prolog will see if the first condition applies, and if so it will start calculating the corresponding definition, which will probably involve some mixture of calls to other functions; it will execute those functions in a similar way. However, it will also "remember where it got up to" during the execution of 'main'; if the definition fails (or if we've asked for more than one result!), it will "back track" and try the next clause. This sort of like having a try/catch in each switch clause, and moving on to the next clause if there's an exception.
Strand seems similar, but rather than trying one clause at a time, depth-first; it instead runs all of the clauses concurrently. This doesn't need to "remember" anything or back-track; if a particular clause fails then its thread dies, leaving the others (if any) to keep going.
It seems like a remarkably simple way to achieve massive parallelism, especially for those already familiar with logic programming.
Parlog86 and the dining logicians G. A. Ringwood Communications of the ACM January 1988 https://doi.org/10.1145/35043.35044
三個和尚沒水å–
(Chinese Proverb)