List is the most important and most often used structured data type in functional programming languages. List is a sequential type, which means that a list may contain many elements in an ordered sequence. In Wafl, a list can consist of elements of any type, but all elements of any specific list must have the same type.
Because of the importance of the list processing concept, it is suggested that this chapter is carefully studied. The discussed functions are essential for programming in Wafl.
The general list type is written as List['1]
, where '1
denotes a type variable (a pure polymorphic type). Each specific list in a program must have a specialized type, where this free type is fully determined.
A list without elements is called empty list. An empty list is written as nil
or []
. These two syntaxes are equivalent.
Nonempty list literals are written as elements enclosed in square brackets and separated by commas.
{#
[],
nil,
[1,2,3], // List[Int]
['a','b','c','d'], // List[String]
[[1,2],[3,4,5],[6,7,8]] // List[List[Int]]
#}
{# [], [], [1, 2, 3], ['a', 'b', 'c', 'd'], [[1, 2], [3, 4, 5], [6, 7, 8]] #}
In the previous example we have the following lists:
[]
;nil
;The processing of the list elements is often explained in terms of processing the list head and the list tail. The head represents the first element of the list and tail represents all but the first elements of the list. Both head and tail are meaningful only for non-empty list.
Basic list functions include elementary operations for checking the list size, extracting its head, tail or sub-list, constructing a list and comparing two lists.
Function / Type and Description
A = B
('1 * '1 -> Bool)
Equality comparison operator.
A != B
('1 * '1 -> Bool)
Inequality comparison operator.
empty
(Indexable['1]['2]['3] -> Bool)
Check if the collection is empty.
length
(Indexable['1]['2]['3] -> Int)
Get the collection size.
longerThan
(List['1] * Int -> Bool)
Check if list has more elements than the given integer.
size
(Indexable['1]['2]['3] -> Int)
Get the collection size.
hd
(List['1] -> '1)
Get a list head. Not defined on empty list.
tl
(List['1] -> List['1])
Get a list tail.
A : B
('1 * List['1] -> List['1])
List construction operator.
It may be strange to see a type like Indexable['1]['2]['3]
, but there is nothing special with it. It is any of the indexable collections (where '1
may be one of the indexable collections base, like list, array, map and string) with index of type '2
and elements of type '3
. So, this type may reduce to List['3]
.
We could replace the types in the tables, but it is good to adapt to the notation.
Lists are comparable for equality. The operators ==
and !=
evaluate the expected results:
{#
[] = nil,
[] != nil,
[] = [1,2,3],
[] != [1,2,3],
[1,2,3] = [1,2,3],
[1,2,3] = [1,2,3,4,5],
[1,2,3] != [1,2,3],
[1,2,3] != [1,2,3,4,5]
#}
{# true, false, false, true, true, false, false, true #}
empty
To check if a list is empty, we can use function empty(l)
or compare the list to the empty list literal.
{#
empty( [] ),
empty( nil ),
empty( [1,2,3] ),
[] = nil
#}
{# true, true, false, true #}
size
, length
and longerThan
Function length(l)
computes the length of the list. Function size(l)
is a synonym.
It is planned to keep both in the future. size
is more general, but length
is more natural for sequences.
{#
length( [] ),
length( nil ),
length( [1,2,3] ),
size( [1,2,3] )
#}
{# 0, 0, 3, 3 #}
In some cases, it is not efficient to compute the length of a list. For example, if a list represents a database query result, then the length is not computable until the complete result is fetched from the database. In such cases it is often useful to just check if the list length is greater than a number. Function longerThan(lst,n)
checks if the lst
is longer than n
and return true
if it is.
{#
longerThan( [1,2,3], 0 ),
longerThan( [1,2,3], 1 ),
longerThan( [1,2,3], 2 ),
longerThan( [1,2,3], 3 ),
longerThan( [1,2,3], 4 ),
longerThan( [1,2,3], 5 )
#}
{# true, true, true, false, false, false #}
hd
, tl
and A:B
Function hd(l)
computes the first element of the given nonempty list. It is not defined on empty lists.
Function tl(l)
computes the tail of the given nonempty list. The list tail is a list suffix consisting of all list elements except the first one. It is not defined on empty lists.
Infix binary operator h:l
constructs a list with head h
and tail t
. The operator type is ('1 * List['1] -> List['1])
. It is usual in functional programming languages to have a function cons
(constructs a list) equivalent to this operator. There is no cons
function in Wafl, but we will often call operator ‘:
’ cons-operator.
{#
hd( [1,2,3] ),
tl( [1,2,3] ),
1:[2,3],
1:2:3:nil,
[2,3]:nil
#}
{# 1, [2, 3], [1, 2, 3], [1, 2, 3], [[2, 3]] #}
List elements are usually processed using recursion:
We will try to apply this concept in few examples.
Let’s sum the elements of an integer list by applying the presented concept:
[1,2,3,4,5].sum()
where {
sum( lst ) =
if lst.empty() then 0
else lst.hd() + lst.tl().sum();
}
15
Let’s compute a list of squares of natural numbers from 1 to 10. We may start from a list [1,2,...,10]
and use a recursive function squares
:
squares
of the empty list is an empty list;squares
of the tail.[1,2,3,4,5,6,7,8,9,10].squares()
where {
squares( lst ) =
if lst.empty() then []
else lst.hd() * lst.hd() : lst.tl().squares();
}
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
The same result may be computed based on the same idea but without explicitly writing a list literal:
squares(1,10)
where {
squares( from, to ) =
if from > to
then []
else from * from : squares( from + 1, to );
}
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Like in the previous example, it is often useful to have a list of integers in a specific range. In this example the function mklst(n,m)
computes the list of integers from n
to m
:
mklst(100,110)
where{
mklst(n,m) =
if n>m then []
else n : mklst( n+1,m);
}
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
In the examples of the previous section we may recognize some repeatability. Let’s focus on two topics:
It is usual to approach these two topics by two different functions. First, we will write the function processElements
that processes each of the list elements and return the results in a new list in the same preserved order. Then, we will write function combineElements
that combines the elements of the result list as we need.
Function processElements
should have two arguments: the list of the elements to process and the processing function. This function is much like presented squares
, so let’s test it by implementing the same thing, by applying it to the function \x:x*x
:
[1,2,3,4,5].processElements( \x: x*x )
where {
processElements( lst, fun ) =
if lst.empty() then []
else lst.hd().fun() : lst.tl().processElements( fun );
}
[1, 4, 9, 16, 25]
Function combineElements
should have three arguments: the list of the elements to combine, the function describing one combining step and the starting value. Let’s test it on the summing - the function is the operator+
and the starting value is zero:
[1,2,3,4,5].combineElements( operator+, 0 )
where {
combineElements( lst, fun, value ) =
if lst.empty() then value
else lst.hd().fun( lst.tl().combineElements( fun, value ) );
}
15
Now we can use both functions for summing the squares of the numbers in a given list:
[1,2,3,4,5]
.processElements( \x: x*x )
.combineElements( operator+, 0 )
where {
processElements( lst, fun ) =
if lst.empty() then []
else lst.hd().fun() : lst.tl().processElements( fun );
combineElements( lst, fun, value ) =
if lst.empty() then value
else lst.hd().fun( lst.tl().combineElements( fun, value ) );
}
55
As we can see, these two functions are very helpful. This is why the equivalents of these two functions, as well as many other higher order functions, are implemented in Wafl core library. The core library function map
is equivalent for processElements
, and foldr
is equivalent for combineElements
.
Some basic list functions are presented in a previous section. Sere we present some more complex list functions, before we deal with higher order functions in the following sections.
Type SequenceStr['2]['1]
includes sequences and strings. Sequences are indexable types with integer indexes.
sub
, subList
Function subList(l,p,n)
computes a sub-list of the given list, beginning at position p
and with n
elements.
Function / Type and Description
sub
(SequenceStr['2]['1] * Int * Int -> SequenceStr['2]['1])
Extracts the subsequence from given 0-based position and given length:
sub(seq,pos,len)
subList
(List['1] * Int * Int -> List['1])
Extracts the sub-list from given 0-based position and given length:
subList(list,pos,len)
It has the same semantics as already presented sub
and subStr
for strings. Moreover, sub
works for lists as well.
{#
sub( [0,1,2,3,4,5,6,7], 0, 3 ),
sub( [0,1,2,3,4,5,6,7], 2, 3 ),
sub( [0,1,2,3,4,5,6,7], -2, 5 ),
sub( [0,1,2,3,4,5,6,7], 5, 10 ),
sub( [0,1,2,3,4,5,6,7], 5, -2 )
#}
{# [0, 1, 2], [2, 3, 4], [], [5, 6, 7], [] #}
Function / Type and Description
A[ : B ]
(SequenceStr['2]['1] * Int -> SequenceStr['2]['1])
Extracts a suffix of the sequence with N elements:
seq[N:]
A[ B : ]
(SequenceStr['2]['1] * Int -> SequenceStr['2]['1])
Extracts a prefix of the sequence with N elements:
seq[:N]
A[ B : C ]
(SequenceStr['2]['1] * Int * Int -> SequenceStr['2]['1])
Extracts a sequence segment from Nth to (M-1)th element:
seq[N:M]
A[ B ]
(Indexable['1]['2]['3] * '2 -> '3)
Extracts an element from the indexable collection:
col[idx]
Index operator lst[i]
and slice operators lst[:n]
, lst[:n]
and lst[n:m]
have the same semantics as for strings, so we will not discuss it in details here.
Note that indexing operator is not defined on empty lists.
{#
lst[-4], lst[-3], lst[-2], lst[-1],
lst[0], lst[1], lst[2], lst[3],
lst[4], lst[6], lst[7], lst[8]
#}
where {
lst = [0,1,2,3];
}
{# 0, 1, 2, 3, 0, 1, 2, 3, 0, 2, 3, 0 #}
{#
lst[:6],
lst[:-2],
lst[2:],
lst[-6:],
lst[2:6],
lst[2:-2],
lst[-6:6],
lst[-6:-2]
#}
where {
lst = [1,2,3,4,5,6,7,8];
}
{# [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8], [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6] #}
append
and A ++ B
Function / Type and Description
A ++ B
(List['1] * List['1] -> List['1])
Append the second list elements to the first one.
append
(List['1] * List['1] -> List['1])
Append the second list elements to the first one.
Function append(l,r)
computes a list containing all elements of the two lists in the preserved order. Operator ‘++
’ is a synonym with infix operator notation:
{#
[1,2,3] ++ [4,5,6],
append( [1,2,3], [4,5,6] )
#}
{# [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6] #}
A..B
, intRange
, intRangeBy
Function / Type and Description
A .. B
(Int * Int -> List[Int])
Create a list of integers in the range:
2..5 = [2,3,4,5]
intRange
(Int * Int -> List[Int])
Create a list of integers in the range:
intRange(2,5) = [2,3,4]
intRangeBy
(Int * Int * Int -> List[Int])
Create a list of integers in the given range with a given step:
intRange(2,9,2) = [2,4,6,8]
intRangeWithLast
(Int * Int -> List[Int])
Create a list of integers in the range:
intRangeWithLast(2,5) = [2,3,4,5]
Integer range operator n..m
computes the list of integers from n
to m
, including both limits. If n
< m
, the elements will be in the increasing order, ant if n
> m
, the elements will be in the decreasing order:
{#
0..10,
-5..5,
5..-5
#}
{# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5] #}
No parenthesis nor brackets are required when using the operator A..B
. Moreover, if brackets are used, the [1..2]
will compute [[1,2]]
and not the expected [1,2]
.
Function intRangeWithLast
is a synonym for the operator A..B
.
Function intRange(n,m)
is similar, but will not include the end of the range m
in the list.
Function intRangeBy(n,m,d)
creates a list of integers from n
to m
but uses a step d
instead of 1. As with intRange
, its result does not include the ending m
value.
{#
5..9,
intRange(5,9),
intRangeBy(5,9,2),
intRangeWithLast(5,9)
#}
{# [5, 6, 7, 8, 9], [5, 6, 7, 8], [5, 7], [5, 6, 7, 8, 9] #}
map
, mapIdx
and zipWith
Function / Type and Description
map
(Sequence['2]['1] * ('1 -> '3) -> Sequence['2]['3])
Map the function to sequence elements:
map(seq,fun)
mapIdx
(Sequence['1]['2] * (Int * '2 -> '3) -> Sequence['1]['3])
Map the function to sequence elements:
mapIdx(seq,fun)
zipWith
(Sequence['4]['1] * Sequence['4]['2] * ('1 * '2 -> '3) -> Sequence['4]['3])
Zips two sequences with given function:
zipWith(seq1, seq2 ,fun)
map
The function map( lst, fun )
is one of the most important higher order functions. It maps the list lst
to a new list with the same number of elements. Each element x
of the lst
is mapped to fun(x)
element of the new list, in the same order.
This function is equivalent to our function processElements
from the previous examples.
In the following example map
is used to compute a list of elements increased by one:
[1,2,3,4,5,6,7,8,9,10].map( \x: x+1 )
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
The same result:
[1,2,3,4,5,6,7,8,9,10].map( operator+(_,1) )
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
We will use map
in many of the following examples.
mapIdx
Sometimes it may be useful to know at what position in the list is the processed element. We can achieve that by transforming (mapping) a list of values to a list of pairs (index, value) and than mapping that list of pairs where indexes are available. However, it is more efficient to have a specific function mapIdx(lst,fun)
, where fun(idx,x)
maps the value x
at position idx
.
['a','b','c','d','e','f','g','h']
.mapIdx( \i,x: x + i.asString() )
['a0', 'b1', 'c2', 'd3', 'e4', 'f5', 'g6', 'h7']
zipWith
Function zipWith(lst1,lst2,fun)
returns a list where each element is a result of the fun(x,y)
applied to the corresponding elements of the two given lists lst1
and lst2
.
zipWith( ['a','b','c'], ['A','B','C'], operator+ )
['aA', 'bB', 'cC']
The mapIdx
function can be defined using zipWith
as follows:
['a','b','c','d','e','f','g','h']
.mapIdx( \i,x: x + i.asString() )
where {
mapIdx( lst, fun ) = (0 .. lst.length()-1).zipWith( lst, fun );
}
['a0', 'b1', 'c2', 'd3', 'e4', 'f5', 'g6', 'h7']
foldr
and foldl
Function / Type and Description
foldr
(Sequence['3]['2] * ('2 * '1 -> '1) * '1 -> '1)
Right associative folding of sequence elements:
foldr([a,b,c],fn,zero) = fn(a,fn(b,fn(c,zero)))
foldl
(Sequence['3]['2] * ('1 * '2 -> '1) * '1 -> '1)
Left associative folding of sequence elements:
foldl([a,b,c],fn,zero) = fn(fn(fn(zero,a),b),c)
foldr
Like the map
, function foldr( lst, fun, zero )
is one of the most important higher order functions. It combines the lst
elements, using zero
as a starting value, and applying fun(x,res)
co combine the element x
with the result res
of already completed combining.
This function is equivalent to our function combineElements
from the previous examples. So let’s use it in an equivalent example:
[1,2,3,4,5].foldr( operator+, 0 )
15
To better understand what foldr
evaluates, consider the following example:
['1','2','3','4','5'].foldr( \x,res: '('+x+','+res+')', '' )
(1,(2,(3,(4,(5,)))))
As we can see, foldr
combines elements by combining the list head with already combined list tail. Thus, it really begins the combining from the end of the list, or from the right side, which is why it has an r
at the end of the name.
foldl
If the combining function fun
as an associative one (i.e. fun(fun(a,b),c)
= fun(a,fun(b,c))
), then the order of combining is not important. But for non-associative functions, like in the previous example the order is important. This is why we have foldl
.
Function foldl
combines the list elements from the beginning. It first combines zero
and the list head, and then combines the result with the next list element and so on. Consider the following example and compare its result to the previous example, where foldr
was used with the same arguments:
['1','2','3','4','5'].foldl( \x,res: '('+x+','+res+')', '' )
(((((,1),2),3),4),5)
If the result of the combining operation has the same type as the list elements, then the combining function has both arguments and the result of the same type. As we already emphasized, if the combining function is associative, then foldr
and foldl
evaluate the same result. Here we can confirm that for addition:
{#
['1','2','3','4','5'].foldr( operator+, '' ),
['1','2','3','4','5'].foldl( operator+, '' )
#}
{# '12345', '12345' #}
On the other side, if the function is not associative, but has the same argument types, then the result will not be the same:
{#
lst.foldr( fun, '' ),
lst.foldl( fun, '' )
#}
where {
lst = ['1','2','3','4','5'];
fun(x,y) = '(' + x + ',' + y +')';
}
{# '(1,(2,(3,(4,(5,)))))', '(((((,1),2),3),4),5)' #}
As we can see, foldl
combines elements by combining the given previous result with the list head, assuming that the result will be passed further to the tail, for the same processing. Thus, it really begins the combining from the beginning of the list, or from the left side, which is why it has an l
at the end of the name.
There are many combining functions that cannot be used with both foldr
and foldl
. If the result of the combining operation has a different type than the list elements, then the used combining function has to have different argument types, also. Such functions cannot be used with both foldr
and foldl
.
For example, let us write the append2
function that appends two lists (the equivalent to the library function append
and operator ++
). We can do that by combining the elements of the first list using cons-operator, and using the second list as a zero
. Because cons-operator is not associative (moreover, it has the different argument types) it is only possible to use foldr
:
[1,2,3].foldr( operator:, [4,5,6])
[1, 2, 3, 4, 5, 6]
In a similar way, we may append many lists:
[[1,2],[3,4],[5,6]].foldr( append, [])
[1, 2, 3, 4, 5, 6]
To use the cons-operator with foldl
we need to reverse the arguments. But the result of the application will be a reversed list:
[1,2,3,4,5,6].foldl( \x,y: y:x, [] )
[6, 5, 4, 3, 2, 1]
The following example illustrates how to use foldr
to compute a list of distinct elements of a sorted list. The combining lambda function assumes that the second argument is the already processed tail:
(1..100)
.map( operator%(_,10) )
.sort()
.distinct()
where {
distinct( lst ) =
lst.foldr(
\x,dist:
if dist.empty() or x != dist.hd()
then x : dist
else dist
, []
);
}
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
filter
, find
and filterMap
Function / Type and Description
filter
(Sequence['2]['1] * ('1 -> Bool) -> Sequence['2]['1])
Extracts the sequence elements which satisfy the given condition.
filterN
(Sequence['2]['1] * ('1 -> Bool) * Int -> Sequence['2]['1])
Extracts at most N elements which satisfy the given condition.
find
(Sequence['2]['1] * ('1 -> Bool) -> Sequence['2][Int])
Finds the indexes of the elements which satisfy the given condition.
findN
(Sequence['2]['1] * ('1 -> Bool) * Int -> Sequence['2][Int])
Find at most N indexes of the elements which satisfy the given condition.
filterMap
(Sequence['3]['1] * ('1 -> Bool) * ('1 -> '2) -> Sequence['3]['2])
Maps a function to sequence elements which satisfy the condition.
mapFilter(seq,cond,fn) == map(filter(seq,cond),fn)
filter
Function filter(lst,cond)
computes the list of lst
elements that satisfy the given condition cond(x)
. The element order is preserved.
{#
[1,2,3,4,5]->filter( operator>(_,1)), // numbers > 1
[1,2,3,4,5]->filter( \x : x%2 > 0 ), // even numbers
[1,2,3,4,5]->filter( \x : x%2 = 0 ) // odd numbers
#}
{# [2, 3, 4, 5], [1, 3, 5], [2, 4] #}
filterN
Function filterN(lst,cond,n)
computes the list of lst
elements that satisfy the given condition cond(x)
, but it selects at most the first n
elements satisfying the condition.
The element order is preserved.
{#
[1,2,3,4,5]->filterN( operator>(_,1), 2 ), // numbers > 1
[1,2,3,4,5]->filterN( \x : x%2 > 0, 1 ), // even numbers
[1,2,3,4,5]->filterN( \x : x%2 = 0, 1 ) // odd numbers
#}
{# [2, 3], [1], [2] #}
find
Function find(lst,cond)
computes the list of zero-based indexes of lst
elements that satisfy the given condition cond(x)
.
The indexes are selected in the increasing order.
{#
[1,2,3,4,5]->find( operator>(_,1)), // numbers > 1
[1,2,3,4,5]->find( \x : x%2 > 0 ), // even numbers
[1,2,3,4,5]->find( \x : x%2 = 0 ) // odd numbers
#}
{# [1, 2, 3, 4], [0, 2, 4], [1, 3] #}
findN
Function findN(lst,cond,n)
computes the list of zero-based indexes of lst
elements that satisfy the given condition cond(x)
, but it selects at most the first n
elements satisfying the condition.
The indexes are selected in the increasing order.
{#
[1,2,3,4,5]->findN( operator>(_,1), 2 ), // numbers > 1
[1,2,3,4,5]->findN( \x : x%2 > 0, 1 ), // even numbers
[1,2,3,4,5]->findN( \x : x%2 = 0, 1 ) // odd numbers
#}
{# [1, 2], [0], [1] #}
filterMap
Function filter
is often used to select some list elements before they are processed by map
. This is why filterMap
function is included, to do both the filtering and the processing in a same step.
Function filterMap(lst,filtFun,mapFun)
is equivalent to the corresponding combination of filter
and map
. The following example computes a list of squares of odd list elements:
{#
list.filterMap( odd, square ),
list.filter(odd).map(square)
#}
where {
list = 1..20;
odd( n ) = n % 2 != 0;
square( n ) = n * n;
}
{# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361], [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] #}
Function filterMap
does not make the program code more readable, but it is more efficient, because it does not create a temporary list, as when separate filter
and map
are used.
forall
, exists
and count
Function / Type and Description
forall
(Sequence['2]['1] * ('1 -> Bool) -> Bool)
Check if the given condition applies to all sequence elements:
forall(list,cond).
exists
(Sequence['2]['1] * ('1 -> Bool) -> Bool)
Check if there is any sequence element satisfying the condition:
exists(sequence,cond)
count
(Sequence['2]['1] * ('1 -> Bool) -> Int)
Count the sequence elements satisfying the condition:
count(seq,cond)
Function forall(lst,cond)
checks if all list elements satisfy the given condition cond(x)
. It is equivalent to:
forall( lst , cond ) == lst.foldr( operator&&, true )
Function exists(lst,cond)
checks if any list element satisfies the given condition cond(x)
. It is equivalent to:
exists( lst , cond ) == lst.foldr( operator||, false )
Function count(lst,cond)
counts how many list elements satisfy the given condition cond(x)
.
{#
[1,2,3,4,5].forall( operator>(_,1) ),
[1,2,3,4,5].forall( operator>=(_,1) ),
[1,2,3,4,5].exists( operator<(_,1) ),
[1,2,3,4,5].exists( operator<=(_,1) ),
[1,2,3,4,5].count( operator>(_,2) ),
[1,2,3,4,5].count( operator>=(_,2) )
#}
{# false, true, false, true, 3, 4 #}
sort
and sortBy
Function / Type and Description
sort
(Sequence['2][Value['1]] -> Sequence['2][Value['1]])
Sort value sequence.
sortBy
(Sequence['2]['1] * ('1 * '1 -> Bool) -> Sequence['2]['1])
Sort the sequence using given comparator.
Function sort(lst)
return list sorted in increasing order. It works for sequences with elements of primitive types.
Function sortBy(lst,lessThan)
return list sorted in increasing order, using the given comparison function lessThan(x,y)
. It works for sequences with any element types, if a corresponding comparison function is provided.
{#
[2,1,4,3,5].sort(),
[2,1,4,3,5].sortBy( operator< ),
[2,1,4,3,5].sortBy( operator> )
#}
{# [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [5, 4, 3, 2, 1] #}
in
and contains
Function / Type and Description
in
('1 * Sequence['2]['1] -> Bool)
Checks if the element is in the sequence:
el.in(seq)
contains
(Sequence['2]['1] * '1 -> Bool)
Checks if the element is in the sequence:
seq.contains(el)
Function in(x,lst)
checks if the list lst
contains x
. It is usually applied in a form x.in(lst)
. It works for sequences of any type.
{#
1 .in([1,2,3]),
11 .in([1,2,3]),
{a:5}.in([ {a:4}, {a:5} ]),
{a:6}.in([ {a:4}, {a:5} ])
#}
{# true, false, true, false #}
Function contains
does the same, but with reversed operand order: contains(lst,x)
is the same as in(x,lst)
.
{#
[1,2,3].contains( 1 ),
[1,2,3].contains( 11 ),
[ {a:4}, {a:5} ].contains( {a:5} ),
[ {a:4}, {a:5} ].contains( {a:6} )
#}
{# true, false, true, false #}
Wafl is an eager language, but like in the most of the languages, there are some lazy elements to prevent unnecessary computations. One such lazy element is the fetching of the database query results. The query result is a lazy list - from the database server only as much rows are fetched as required in the computation.
However, sometimes we know that we need to process all rows, but that may take some time and it may be useful to finish the communication with the database server as soon as possible. In such cases we can require the eager evaluation of the query list using the force
function.
Function / Type and Description
forced
(List['1] -> List['1])
Returns the list, but forces any delayed evaluation.
Function force(lst)
returns the same list, but forces its eager evaluation.