core

The foundational library: list operations, folds, higher-order helpers, and type conversions.

import core

Most programs start here. core is written in Bern itself, so every function below is an ordinary Bern function you could read in lib/core.brn. Functions that walk a list take the list as their first argument, which makes them a natural fit for the pipe operator )|.

Transforming lists

map(list, function) → list

Apply a function to every element, returning a new list of the results.

map([1, 2, 3], \x -> x * 2)
-- Output: [2, 4, 6]
filter(list, predicate) → list

Keep only the elements for which the predicate returns true.

filter([1, 2, 3, 4, 5], \x -> x % 2 == 0)
-- Output: [2, 4]
reverse(collection) → collection

Reverse a list or a set; the type is detected automatically.

reverse([1, 2, 3])
-- Output: [3, 2, 1]

reverse({1, 2, 3})
-- Output: {3, 2, 1}
append(list, tail) → list

Concatenate tail onto the end of list (the same effect as <>, expressed as a function).

append([1, 2], [3, 4])
-- Output: [1, 2, 3, 4]
range(start, end) → list

Build an inclusive list of integers - a function wrapper around the [start..end] syntax.

range(1, 5)
-- Output: [1, 2, 3, 4, 5]

Folding & aggregating

foldl(list, accumulator, function) → value

Reduce a list left-to-right. The function receives (accumulator, element).

foldl([1, 2, 3], 0, \acc, x -> acc + x)
-- Output: 6
foldr(list, accumulator, function) → value

Reduce a list right-to-left. The function receives (element, accumulator), so it is the natural choice when you want to rebuild a list from the right.

foldr([1, 2, 3], [], \x, acc -> [x * 10] <> acc)
-- Output: [10, 20, 30]
sum(list) → number

Add every element together.

sum([1, 2, 3, 4])
-- Output: 10
product(list) → number

Multiply every element together.

product([1, 2, 3, 4])
-- Output: 24
min(list) → value

The smallest element. Errors on an empty list.

min([4, 1, 7, 3])
-- Output: 1
max(list) → value

The largest element. Errors on an empty list.

max([4, 1, 7, 3])
-- Output: 7

Slicing & access

take(n, list) → list

The first n elements (or the whole list if it is shorter).

take(2, [10, 20, 30, 40])
-- Output: [10, 20]
drop(n, list) → list

Everything except the first n elements.

drop(2, [10, 20, 30, 40])
-- Output: [30, 40]
head(list) → element

The first element. Returns an error value for an empty list.

head([10, 20, 30])
-- Output: 10
tail(list) → list

Everything except the first element.

tail([10, 20, 30])
-- Output: [20, 30]

Predicates & search

isEmpty(list) → boolean

Whether the list has no elements.

isEmpty([])
-- Output: true
isEmpty([1])
-- Output: false
any(list, predicate) → boolean

Does at least one element satisfy the predicate?

any([1, 3, 5, 6], \x -> x % 2 == 0)
-- Output: true
all(list, predicate) → boolean

Do all elements satisfy the predicate?

all([2, 4, 6], \x -> x % 2 == 0)
-- Output: true
find(list, predicate) → element | false

The first matching element, or false when none match.

find([1, 2, 3, 4], \x -> x > 2)
-- Output: 3
find([1, 2], \x -> x > 9)
-- Output: false
index_of(collection, target) → int

The index of target in a list or set, or -1 if it is absent.

index_of(['a', 'b', 'c'], 'b')
-- Output: 1
index_of([1, 2, 3], 9)
-- Output: -1

Combining lists

zip(list1, list2) → list

Pair up elements by position; stops at the shorter list.

zip([1, 2, 3], ['a', 'b', 'c'])
-- Output: [[1, 'a'], [2, 'b'], [3, 'c']]
zipWith(list1, list2, function) → list

Combine elements by position using a two-argument function.

zipWith([1, 2, 3], [10, 20, 30], \a, b -> a + b)
-- Output: [11, 22, 33]

Function helpers

compose(f, g, x) → value

Apply g then f: compose(f, g, x) is f(g(x)).

compose(\x -> x + 1, \x -> x * 2, 5)
-- Output: 11   (5 * 2, then + 1)
id(x) → x

The identity function - returns its argument unchanged. Handy as a default or placeholder.

id(42)
-- Output: 42
const(x, _) → x

Always returns its first argument and ignores the second.

const(7, "ignored")
-- Output: 7
flip(f, x, y) → value

Call a two-argument function with its arguments swapped.

flip(\a, b -> a - b, 3, 10)
-- Output: 7   (10 - 3)

Conversions & introspection

to_int(string) → int

Parse a string of digits into an integer.

to_int("42")
-- Output: 42
int_to_double(int) → double

Widen an integer to a double.

int_to_double(5)
-- Output: 5.0
char_to_digit(char) → int

Turn a digit character into its numeric value (errors on a non-digit).

char_to_digit('7')
-- Output: 7
typeOf(value) → string

The type of a value as a string - a function form of the :: operator.

typeOf([1, 2, 3])
-- Output: "List"
length(value) → int

The length of a string, list, or set - a function form of the :> operator.

length("hello")
-- Output: 5
is_error(value) → boolean

Whether a value is a recoverable error value.

is_error([1, 2, 3][99])
-- Output: true
print(value) → value

Explicitly print a value (literals print on their own, but print is useful under the no-eval pragma). Returns the value.

print("hello")
-- Output: "hello"

Putting it together

The functions compose naturally. Here several are chained with the pipe operator to turn a list of prices into the total of the discounted, in-stock ones:

A small data pipeline

import core

prices = [12, 40, 7, 25, 3, 60]

-- keep the affordable ones, apply a 10% discount, then total them
total = prices
    )| filter(\p -> p <= 40)
    )| map(\p -> p - (p / 10))
    )| sum

total
-- Output: 76   (12+40+7+25 = 84, less 10% per item)

And a classic fold-based summary that reuses zip, foldl, and max together:

Labelling the highest score

import core

names  = ["Ana", "Bob", "Cia"]
scores = [88, 95, 73]

pairs = zip(names, scores)
-- [["Ana", 88], ["Bob", 95], ["Cia", 73]]

best = max(scores)
winner = find(pairs, \pair -> pair[1] == best)
winner[0] + " won with " + best
-- Output: "Bob won with 95"