Lists
1 :: 2 :: 3 :: 4 :: []
[1, 2, 3, 4]
cons(1, cons(2, cons(3, cons(4, nil()))))
[1, 2] ++ [3, 4]
The list type constructor is [a]
where ‘a
’ is a type.
The empty list is can be written two ways:
[]
nil()
The cons operator as well:
<Expr :: a> :: <Expr :: [a]>
cons(<Expr :: a>, <Expr :: [a]>)
The append operator (++
) is usable on lists, as is ‘length
':
<Expr :: [a]> ++ <Expr :: [a]>
length(<Expr :: [a]>)
Finally, there are three more function that operate on lists:
null( <Expr :: [a]> )
head( <Expr :: [a]> )
tail( <Expr :: [a]> )
null
returns true if and only if the list is empty (i.e. nil). head
returns the first element of the list, while tail
returns the remainder of the list, after the first element.
Note: If
head
ortail
are called on an empty list, an uncatchable error is raised. In nearly every case, pattern matching should be used instead of these functions.
Yes, ::
is both the type operator and the cons operator in Silver. Here’s a helpful guide:
Language | Scope resolution operator | Has type operator | list cons operator | Access operator |
---|---|---|---|---|
Silver | : | :: | :: | . |
Haskell | . | :: | : | n/a |
Ocaml, Scala, F# | . | : | :: | . |
It wasn’t confusing enough that Haskell uses ::
as the type operator and :
as the cons operator, while ML uses the exact oppose. We hope that our strategy of using one operator for both will sweep the world, though another language will inevitably use only :
, just to annoy everyone in classic Bieber-like fashion. The parser is lonely, please give it some syntax errors to play with.
Alright, seriously now. It’d be nice if we could eliminate this overlap, but we really wanted to keep the access operator and the scope resolution operator syntactically distinct.
Consider
Foo.Bar.Baz
in Java, and that each of those names could be a package, a class, a field, or…. the only option to make sense of it is to go left-right looking up each name in turn.Foo:Bar.Baz
is the grammarFoo
, a valueBar
, and the attributeBaz
, no question.
This becomes dramatically more important when you realize that attributes can be scoped, too. Consider
foo:bar.foo:baz
.
Unfortunately, no other operator really seems appropriate for any of these cases. Fortunately, there is no ambiguity in Silver, because arbitrary expressions don’t need to be given explicit type annotations. For now.
Applies the function
f
to each element of the listl
, and returns the resulting list.
function map
[b] ::= f::(b ::= a) l::[a]
Example:
map(null, [[],[1],[]])
will produce the list
[true, false, true]
.
Starting with the initial value
i
, the functionf
is applied to the current intermediate value and the next element of the list, and the last intermediate value is returned as the result. If the list is empty, the initial value is also the result.
function foldr
b ::= f::(b ::= a b) i::b l::[a]
Example:
foldr(stringConcat, "He", ["llo", ", ", "World!"])
will produce the string
"Hello, World!"
.
The function
f
is applied to each element listlst
, and the element is included in the final list only if the function returns true. The order of elements is preserved.
function filter
[a] ::= f::(Boolean ::= a) lst::[a]
Example:
function isSilverFile
Boolean ::= s::String
{
return endsWith(".sv", s) || endsWith(".sv.md", s);
}
silverFiles = filter(isSilverFile, directoryContents);
will assign to
silverFiles
only those files that end in.sv
or.sv.md
.
Returns true if an only if the element
elem
is in the listlst
, as determined by the equality functioneq
.
function containsBy
Boolean ::= eq::(Boolean ::= a a) elem::a lst::[a]
Example:
containsBy(stringEq, "ABC", ["A", "B", "C"])
will return
false
.
Returns the last element of the list. Ensure that the list is not empty, or it will raise an error (as
head
does on an empty list.)
function last
a ::= lst::[a]
Example:
last(["A", "B", "C"])
will return
"C"
.
Returns the list remaining after
number
elements have been removed from the beginning. If the list does not have that many elements, the empty list is returned. Or, returns the list remaining after the functionf
returns false for the first time (including the element it returned false on.)
function drop
[a] ::= number::Integer lst::[a]
function dropWhile
[a] ::= f::(Boolean::=a) lst::[a]
Example:
drop(2, ["A", "B", "C"])
will return
["C"]
.
Returns the first
number
elements of the list. Or the whole list if there aren’t that many elements in the list. Or, returns the list up to the first element thatf
returns false on (excluding the element that returns false.)
function take
[a] ::= number::Integer lst::[a]
function takeWhile
[a] ::= f::(Boolean::=a) lst::[a]
Example:
take(2, ["A", "B", "C"])
will return
["A","B"]
.
Returns the reverse of the list.
function reverse
[a] ::= lst::[a]
Example:
head(reverse(lst)) == last(lst)
will always return true, or raise an error on an empty list.
Sorts the list according to the comparison function
lte
.
function sortBy
[a] ::= lte::(Boolean ::= a a) lst::[a]
Note: If
lte
is a <= comparison, the sort will be stable and in ascending order. If it is a >= comparison, the sort will be stable and descending.
Example:
sortBy(stringLte, ["b","c","a"])
will return
["a","b","c"]
.