Protelog is an evolving collection of more-and-less experimental
Prolog syntax extensions. The name is meant to evoke the protean
nature of the project. Each syntax extension is collected in its own
module which is then reexported by the protelog
module; thus, users
can sprinkle on the sugar sparingly or mix it in all at once, as they
see fit.
Module capture_compounds.pl
exports a single operator, @/2
, which
facilitates syntax akin to Haskell's @
or OCaml's as
. I.e., it
enables destructuring compound data structures and binding them to a
variable simultaneously.
E.g.,
tails([], []).
tails(List@[_|Rest], [List|Tails]) :- tails(Rest, Tails).
Which expands to
tails([], []).
tails([B|C], [A|D]) :-
A=[B|C],
tails(C, D).
@/2
can be used with any terms, not just lists. I think it might
prove useful in meta-programming contexts, when one wants to inspect a
compound term and call the term, but I'm not sure. E.g.,
contrived_call(Pred@p(thing), Arg) :- call(Pred, stuff, Arg).
contrived_call(Pred@q(_,_,_), _) :- Pred.
contrived_call(Pred, Arg) :- call(Pred, Arg).
Module collectors.pl
aims to provide an alternative syntax for aggregating,
collecting, and testing solutions. It provides 3 operators, are/2
,
of/2
, and of_all/2
. are/2
is sugar for findall/3
. of/2
is
combined with special compounds in its left argument to dispatch calls
to aggregate_all/3
and findnsols/3
. of_all/2
facilitates calling
some goal for all solutions of a condition: it is like a for_each/2
that can leave bindings.
Using are/2
we can rewrite
ns(Ns) :-
findall(X,
( between(1,100,N),
N^2 > 3,
X is 2 * N),
Ns).
as
ns(Ns) :-
Ns are { X | between(1, 100, N),
N^2 > 3,
X is 2 * N }.
Using of/2
we can get pretty, declarative idioms for aggregating results:
?- Xs are {X|between(1,10,_), random_between(1,100,X)}.
Xs = [22, 71, 96, 77, 41, 85, 67, 22, 22|...].
?- max(M) of {X | between(1,5,_), random_between(1,100,X)}.
M = 84
?- set(S) of {X | member(X, [1,1,2,3,3,4,5,5,6,7,78,9])}.
S = [1, 2, 3, 4, 5, 6, 7, 9, 78]
?- sorted(S) of {X | between(1,10,_), random(X)}.
S = [0.12897679140720977, 0.20889013811096088, 0.2823282255935682, 0.40329472611490347, 0.40330146937321065, 0.6229976729667204, 0.6893687806662105, 0.8018322312531445, 0.8719945135823282|...]
of_all/2
might be the most powerful of these sugar bits, the virtues of which
might go beyond the aesthetic. I think it might provide a flexible, declarative,
and more general replacement for maplist
?- length(X,8) of_all member(X, [A,B,C,D,E]).
A = [_G2212, _G2215, _G2218, _G2221, _G2224, _G2227, _G2230, _G2233],
B = [_G2239, _G2242, _G2245, _G2248, _G2251, _G2254, _G2257, _G2260],
C = [_G2266, _G2269, _G2272, _G2275, _G2278, _G2281, _G2284, _G2287],
D = [_G2293, _G2296, _G2299, _G2302, _G2305, _G2308, _G2311, _G2314],
E = [_G2320, _G2323, _G2326, _G2329, _G2332, _G2335, _G2338, _G2341].
?- (X = 2) of_all (length(L, 10), member(X, L)).
L = [2, 2, 2, 2, 2, 2, 2, 2, 2|...].
With some refinement and further development, I think the extensions in
collectors.pl
might be a nice addition to an adventurous Prologers toolbox.
Composer is an experiment in LP-friendly, relational programming oriented
compositional syntax. It strives to offer simple syntax sugar for indicating
structures amounting to chains of data transformation. I am not sure that it is
useful, or that it is in any way preferable to the either Michael Hendricks
func
package % or Carlo Capelli's lifter
module.
With composer.pl
, we can rewrite
?- string_lower("ABCDEFG", Lower), string_chars(Lower, Chars), reverse(Chars, RevChars),
string_chars(RevChars, LowerCaseReversed).
as
?- LowerCaseReversed =: inv string_chars <- reverse <- string_chars <- string_lower("ABCDEFG").
LowerCaseReversed = "gfedcba".