________________________________________________________________________

This file is part of Logtalk <https://logtalk.org/>  
Copyright 1998-2020 Paulo Moura <pmoura@logtalk.org>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
________________________________________________________________________


% start by loading the example:

| ?- logtalk_load(lambdas(loader)).
...


% some sample queries using the call/N built-in methods (note that these
% methods are private, hence the use of the context switching <</2 control
% construct):

| ?- logtalk << call([X,Y]>>(Y is X*X), 5, R).
R = 25
yes

| ?- logtalk << call([Z]>>(call([X,Y]>>(Y is X*X), 5, R), Z is R*R), T).
T = 625
yes


% some sample queries using the "metapredicates" library predicates:

| ?- meta::map([X]>>(X>3),[4,5,9]).
yes

| ?- meta::map([A-B,B-A]>>true, [1-a,2-b,3-c], Zs).
Zs = [a-1,b-2,c-3]
yes

| ?- meta::map([A-B]>>([B-A]>>true), [1-a,2-b,3-c], Zs).
Zs = [a-1,b-2,c-3]
yes

| ?- Points = [(1,4),(2,5),(8,3)], meta::map([(X,Y),Z]>>(Z is sqrt(X*X + Y*Y)), Points, Distances).

Distances = [4.1231056256176606,5.3851648071345037,8.5440037453175304]
Points = [(1,4),(2,5),(8,3)]
yes

| ?- Points = [(1,4),(2,5),(8,3)], meta::map([(X,Y)]>>([Z]>>(Z is sqrt(X*X + Y*Y))), Points, Distances).

Distances = [4.1231056256176606,5.3851648071345037,8.5440037453175304]
Points = [(1,4),(2,5),(8,3)]
yes

| ?- meta::map([[X,Y],Z]>>(Z is X*X + Y*Y), [[1,2],[3,4],[5,6]], Result).
Result = [5,25,61]
yes

| ?- meta::map([[X,Y]]>>([Z]>>(Z is X*X + Y*Y)), [[1,2],[3,4],[5,6]], Result).
Result = [5,25,61]
yes

| ?- Xsss = [[[1,2,3],[4]],[[5]]], meta::map(meta::map(meta::map([X,Y]>>(Y is X+3))), Xsss, Ysss).
Xsss = [[[1,2,3],[4]],[[5]]]
Ysss = [[[4,5,6],[7]],[[8]]]
yes

| ?- meta::map([X,[X]]>>true,[1,2],Ys).
Ys = [[1],[2]]
yes

| ?- meta::map([X,[X]]>>true,Xs,[[1],[2]]).
Xs = [1,2] ?
yes

% in the next example, we use currying to "declare" the variable L as a local variable

| ?- meta::map([N]>>({L}/[M]>>(list::length(L, N), list::length([1|L], M))), [999,123],R).
R = [1000,124]
yes


% some sample queries using lambda expressions as goals (not closures):

| ?- logtalk<<([]>>true).
yes

| ?- logtalk<<({}/true).
yes

| ?- logtalk<<({}/[]>>true).
yes

| ?- logtalk<<({X}/true).
yes


% some error cases:

| ?- logtalk << ({X}/[X]>>true).
uncaught exception: error(representation_error(lambda_parameters),{_282}/[_282]>>true,logtalk)

| ?- meta::map({X}/[X]>>char_code(X), [a,b,c], R).
uncaught exception: error(representation_error(lambda_parameters),{a}/[a]>>char_code(a),meta)

| ?- meta::map([X,Y,Z]>>char_code(X), [a,b,c], R).
uncaught exception: error(representation_error(lambda_parameters),[_278,_280,_282]>>char_code(_278),meta)


% some examples with constraints using GNU Prolog as the backend compiler:

| ?- Xss = [[1,2],[3]], meta::map(meta::map([X,Y,Z]>>(X+Y#=Z)), Xss, Yss, Zss).

Xss = [[1,2],[3]]
Yss = [[_#3(0..268435454),_#54(0..268435453)],[_#105(0..268435452)]]
Zss = [[_#22(1..268435455),_#73(2..268435455)],[_#124(3..268435455)]]

(1 ms) yes
| ?- Xss= [[1,2],[3]], meta::map(meta::map({Y}/[X,Z]>>(X+Y#=Z)), Xss, Zss).

Xss = [[1,2],[3]]
Y = _#3(0..268435452)
Zss = [[_#22(1..268435453),_#66(2..268435454)],[_#110(3..268435455)]]

(1 ms) yes

| ?- meta::map({Y}/[X,Z]>>(Z#=X+Y), Xs, Ys).

Xs = []
Ys = [] ? ;

Xs = [_#3(0..268435455)]
Y = _#22(0..268435455)
Ys = [_#41(0..268435455)] ? ;

Xs = [_#3(0..268435455),_#96(0..268435455)]
Y = _#22(0..268435455)
Ys = [_#41(0..268435455),_#115(0..268435455)] ? ;

Xs = [_#3(0..268435455),_#96(0..268435455),_#170(0..268435455)]
Y = _#22(0..268435455)
Ys = [_#41(0..268435455),_#115(0..268435455),_#189(0..268435455)] ? ;

Xs = [_#3(0..268435455),_#96(0..268435455),_#170(0..268435455),_#244(0..268435455)]
Y = _#22(0..268435455)
Ys = [_#41(0..268435455),_#115(0..268435455),_#189(0..268435455),_#263(0..268435455)] ?
...


% some examples with constraints using SWI-Prolog or YAP as the backend compiler:

| ?- use_module(library(clpfd)).

| ?- Xs = [A,B], meta::map({Y}/[X,Z]>>(clpfd:(X+Y #= Z)), Xs, Zs).
Xs = [A, B],
Zs = [_G1114, _G1117],
A+Y#=_G1114,
B+Y#=_G1117.

| ?- meta::map({Z}/[X,Y]>>(clpfd:(Z#=X+Y)), Xs, Ys).
Xs = [],
Ys = [] ;
Xs = [_G1369],
Ys = [_G1378],
_G1369+_G1378#=Z ;
Xs = [_G1579, _G1582],
Ys = [_G1591, _G1594],
_G1582+_G1594#=Z,
_G1579+_G1591#=Z ;
Xs = [_G1789, _G1792, _G1795],
Ys = [_G1804, _G1807, _G1810],
_G1795+_G1810#=Z,
_G1792+_G1807#=Z,
_G1789+_G1804#=Z ;
Xs = [_G1999, _G2002, _G2005, _G2008],
Ys = [_G2017, _G2020, _G2023, _G2026],
_G2008+_G2026#=Z,
_G2005+_G2023#=Z,
_G2002+_G2020#=Z,
_G1999+_G2017#=Z ;
Xs = [_G2209, _G2212, _G2215, _G2218, _G2221],
Ys = [_G2230, _G2233, _G2236, _G2239, _G2242],
_G2221+_G2242#=Z,
_G2218+_G2239#=Z,
_G2215+_G2236#=Z,
_G2212+_G2233#=Z,
_G2209+_G2230#=Z
...


% some examples with constraints using B-Prolog as the backend compiler:

| ?- Xss= [[1,2],[3]], meta::map(meta::map([X,Y,Z]>>(X+Y#=Z)), Xss, Yss, Zss).
Xss = [[1,2],[3]]
Yss = [[_01acd0:[-268435455..268435455],_0348d0:[-268435455..268435455]],[_04e5dc:[-268435455..268435455]]]
Zss = [[_01ac9c:[-268435455..268435455],_03489c:[-268435455..268435455]],[_04e5a8:[-268435455..268435455]]]
yes

| ?- Xss= [[1,2],[3]], meta::map(meta::map({Y}/[X,Z]>>(X+Y#=Z)), Xss, Zss).
Xss = [[1,2],[3]]
Zss = [[_01aca4:[-268435455..268435455],_0348cc:[-268435455..268435455]],[_04e5c4:[-268435455..268435455]]]
yes


% some examples with constraints using SICStus Prolog as the backend compiler:

| ?- use_module(library(clpfd)).

| ?- Xss= [[1,2],[3]], meta::map(meta::map([X,Y,Z]>>(clpfd:(X+Y#=Z))), Xss, Yss, Zss).
Xss = [[1,2],[3]],
Yss = [[_A,_B],[_C]],
Zss = [[_D,_E],[_F]],
_D in inf..sup,
_A in inf..sup,
_E in inf..sup,
_B in inf..sup,
_F in inf..sup,
_C in inf..sup ?
yes

| ?- Xs = [A,B], meta::map({Y}/[X,Z]>>(clpfd:(X+Y #= Z)), Xs, Zs).
Xs = [A,B],
Zs = [_A,_B],
A in inf..sup,
Y in inf..sup,
_A in inf..sup,
B in inf..sup,
_B in inf..sup ?
yes


% examples on simplifying setof/3 and similar predicates usage:

| ?- countries::currencies_wrong(Currencies).
Currencies = [pound_sterling] ;
Currencies = [dinar] ;
Currencies = [ringgit] ;
Currencies = [euro] ;
Currencies = [euro] ;
Currencies = [dinar]
yes

| ?- countries::currencies_no_lambda(Currencies).
Currencies = [dinar, euro, pound_sterling, ringgit].
yes

| ?- countries::currencies_lambda(Currencies).
Currencies = [dinar, euro, pound_sterling, ringgit].
yes


% example of using a custom implementation of fold left in disguise:

| ?- sigma::sum([X,Y]>>(Y is X), 0, 9, R).
R = 45
yes

| ?- sigma::sum([X,Y]>>(Y is X*X), 0, 9, R).
R = 285
yes


% remember that closures are called in the context of the "sender" when
% used as arguments to meta-predicates:

| ?- sigma::sum([X,Y]>>(sigma::sum([W,Z]>>(Z is W), X, 9, Y)), 0, 9, R).
R = 330
yes


% use the fold_left/4 meta-predicate to calculate Fibonacci numbers:

| ?- meta::fold_left([N1-[F1,F2],_,N2-[F2,F3]]>>(F3 is F1+F2, N2 is N1+1), 0-[0,1], _, 10-[F, _]).
F = 55
yes

| ?- meta::fold_left([N1-[F1,F2],_,N2-[F2,F3]]>>(F3 is F1+F2, N2 is N1+1), 0-[0,1], _, N-[F, _]).

F = 0
N = 0 ? ;
F = 1
N = 1 ? ;
F = 1
N = 2 ? ;
F = 2
N = 3 ? ;
F = 3
N = 4 ? ;
F = 5
N = 5 ?
...


% miscellaneous tests of using lambda expressions:

| ?- misc::common_prefix([1], Xs, Ys).

Xs = []
Ys = [] ? ;

Xs = [A]
Ys = [[1|A]] ? ;

Xs = [A,B]
Ys = [[1|A],[1|B]] ? ;

Xs = [A,B,C]
Ys = [[1|A],[1|B],[1|C]] ?
...


| ?- misc::call_n.
This test should print f(x,y) in all lines:
f(x,y)
f(x,y)
f(x,y)
f(x,y)
f(x,y)
f(x,y)
yes


| ?- misc::local.
yes


% the following lambda benchmarks are so far only available when using
% SWI-Prolog, XSB, or YAP as the Logtalk backend compilers:

?- lambda_benchmarks::bench1.
Using map/2 with a closure for testing less(0, X) with X in [1..100000]: 
% 600,004 inferences, 0.091 CPU in 0.095 seconds (95% CPU, 6618178 Lips)
Using map/2 with a lambda for testing less(0, X) with X in [1..100000]:  
% 2,300,030 inferences, 0.479 CPU in 0.481 seconds (100% CPU, 4801322 Lips)
true.

% the second benchmarks is based on code posted by Jan Wielemaker in
% the SWI-Prolog mailing list:

?- lambda_benchmarks::bench2.
Adding 1 to every integer in the list [1..100000] using a local add1/2 predicate:
% 100,002 inferences, 0.015 CPU in 0.016 seconds (93% CPU, 6702547 Lips)
Adding 1 to every integer in the list [1..100000] using map/3 with the integer::plus/3 predicate:
% 700,004 inferences, 0.133 CPU in 0.134 seconds (99% CPU, 5257456 Lips)
Adding 1 to every integer in the list [1..100000] using map/3 with a lambda argument with a is/2 goal:
% 1,800,030 inferences, 0.439 CPU in 0.442 seconds (99% CPU, 4100651 Lips)
true.


% learn about lint checks for lambdas expressions:

| ?- logtalk_load(lambda_warnings).

*     Unclassified variable A in lambda expression: [B,C]>>f(B,C,A)
*       while compiling object lambda_warnings
*       in file .../examples/lambdas/lambda_warnings.lgt between lines 31-33
*     
*     Variable A have dual role in lambda expression: {A}/[B,C,A]>>f(B,C,A)
*       while compiling object lambda_warnings
*       in file .../examples/lambdas/lambda_warnings.lgt between lines 36-37
*     
% [ .../examples/lambdas/lambda_warnings.lgt loaded ]
% 2 compilation warnings
yes
