Tags
October 25, 2023
by
Alexander VerbruggenRead more about this author

Classic Utilities

There is a strong focus in glue to offer a basic toolset of powerful methods rather than a ton of very specific methods.

It is left up to library providers or endusers to craft specific utilities as needed. Here are some example of classic utilities you would expect to find in most languages and how you can achieve them with the core methods in glue.

Push/Unshift/Offer/...

These methods revolve around adding elements at the back or front of a series, they can be achieved using the merge() functionality:

series = series(1, 2, 3)
# push at the end
series = merge(series, 4, 5)
# unshift at the beginning
series = merge(0, series)
echo(series)
[0, 1, 2, 3, 4, 5]

In fact merge will allow you to merge a combination of collections and standalone values.

Pop/Shift/Pull/...

These methods revolve around removing elements at the back or the front of a series, this can be achieved with offset():

series = series(0, 1, 2, 3, 4, 5)
# remove at front
series = offset(1, series)
# remove at back
series = offset(-2, series)
echo(series)
[1, 2, 3]

If we want to write a reusable pop utility, the question becomes what the best return value is. Both the modified list and the removed element are of interest.
Glue supports inherent multiple return, so we could do something like this:

pop = lambda
        [] series ?= null
        @return
        popped = last(series)
        @return
        series = offset(-1, series)

echo(pop(1, 2, 3, 4))
{popped=4, series=[1, 2, 3]}

Where lambdas are unable to affect the original captured context, methods are allowed to do this so we could actually change the series:

series = series(0, 1, 2, 3, 4, 5)

pop = method
        @return
        popped = last(series)
        @persist
        series = offset(-1, series)

echo(pop())
echo(series)
5
[0, 1, 2, 3, 4]

In this case however we are modifying the actual "series" variable in the scope which is not always ideal. We could capture the series in a context though where we expose additional functions:

series = series(1, 2, 3, 4)

managed = lambda
        [] series ?= null
        pop = method
                @return
                popped = last(series)
                @persist
                series = offset(-1, series)

a = managed(series)
echo(a/pop())
echo(a/series)
# print the original series to check that it wasn't altered
echo(series)
4
[1, 2, 3]
[1, 2, 3, 4]

Slice/Sublist/...

The slice functionality in general allows you to take a part of a list, you can do this with a combination of offset() and limit():

series = series(0, 1, 2, 3, 4, 5)
# base it on an initial offset and a number of items
first = limit(3, offset(1, series))
# base it on an initial offset and an end offset
# in this particular case we remove the first and last items of the series
second = offset(-1, offset(1, series))

Splice

The splice functionality allows you to remove parts of a list and/or replace them with some other elements, this can be achieved with a combination of offset(), limit() and merge():

series = series(0, 1, 3, 4)
series = merge(
    limit(2, series),
    2,
    offset(2, series))

At this point the list contains: [0, 1, 2, 3, 4]

Combine it all

You can combine all of the above into a single managed instance:

series = series(1, 2, 3, 4)

managed = lambda
    [] series ?= null
    pop = method
        @return
        popped = last(series)
        @persist
        series = offset(-1, series)
    push = method
        [] values ?= null
        @persist
        series = merge(series, values)
        @return
        value = $this
    slice = method
        from ?= 0
        to ?= size(series)
        @return
        sliced = merge(limit(to - from, offset(from, series)))
    splice = method
        from ?= 0
        to ?= size(series)
        [] elements ?= null
        @return
        sliced = slice(from, to)
        @persist
        series = merge(limit(from, series), elements, offset(to, series))

a = managed(series)

echo("Popped: " + a/pop())
echo(a/push(5, 6)/slice(3, 5))

echo(a/series)
echo(a/splice(1, 3, 7, 8))
echo(a/series)
Popped: 4
[5, 6]
[1, 2, 3, 5, 6]
[2, 3]
[1, 7, 8, 5, 6]

Zip

The python zip() method is a special case of derive():

series1 = series(1, 2, 3)
series2 = series(5, 4, 3)
zip = lambda(x, y, series(x, y))
echo(derive(zip, series1, series2))
[[1, 5], [2, 4], [3, 3]]

You could also implement your own zip() method as follows:

zip = lambda
    [] series ?= null

    helper = sequence
        [] input ?= null
        @return
        result = series(unwrap(input))

    @return
    result = derive(helper, unwrap(series))

echo(zip(series(1, 2, 3),
    series(5, 4, 3)))

echo(zip(series(1, 2, 3),
    series(5, 4, 3),
    series(10, 9, 8)))
[[1, 5], [2, 4], [3, 3]]
[[1, 5, 10], [2, 4, 9], [3, 3, 8]]

Any/All

Given a series of booleans, you can check whether any boolean is true or all booleans are true. For example:

regexes = series("a.*", "b.*")
## Generate a series of booleans
echo("aa" ~ regexes)
## Check if the boolean "true" is in that series (= any)
echo(true ? ("aa" ~ regexes))

## Check that the boolean "false" is not in that series (= all)
echo(false !? ("aa" ~ regexes))

This prints out:

[true, false]
true
false