Home of the ABS programming language: the joy of shell scripting.
This is a bugfix release that fixes a bug in the @util.memoize
decorator. Earlier, the decorator used a shared in-memory map for all functions, meaning that 2 different functions with the same arguments would collide and memoize each other's results (#379).
A fresh new minor release of ABS: always be shipping! :ship:
The @cli.repl()
mode allows to run interactive REPLs based on the commands registered within the CLI:
cli = require('@cli')
res = {"count": 0}
@cli.cmd("count", "prints a counter", {})
f counter(arguments, flags) {
echo(res.count)
}
@cli.cmd("incr", "Increment our counter", {})
f incr(arguments, flags) {
res.count += 1
return "ok"
}
@cli.cmd("incr_by", "Increment our counter", {})
f incr_by(arguments, flags) {
echo("Increment by how much?")
n = stdin().number()
res.count += n
return "ok"
}
cli.repl()
You can see it in action:
$ ./cli
help
Available commands:
* count - prints a counter
* help - print this help message
* incr - Increment our counter
* incr_by - Increment our counter
count
0
incr
ok
incr
ok
count
2
incr_by
Increment by how much?
-10
ok
count
-8
@util.memoize
A new @util
native module was introduced to allow memoization of functions:
memo = require('@util').memoize
@memo(60)
f long_task(x, y) {
sleep(1000)
return "done"
}
echo(long_task(1, 1)) # waits 1s
echo(long_task(1, 1)) # immediate
echo(long_task(1, 1)) # immediate
sleep(61000) # sleep for a min...
echo(long_task(1, 1)) # waits 1s
echo(long_task(1, 2)) # waits 1s
unix_ms()
returns the current Unix epoch, in milliseconds.
unix_ms() # 1594049453157
See ya! :wave:
A fresh new minor release of ABS: always be shipping! :ship:
You can now make a parameter optional by specifying its default value:
f greet(name, greeting = "hello") {
echo("$greeting $name!")
}
greet("user") # hello user!
greet("user", "hola") # hola user!
A default value can be any expression (doesn't have to be a literal):
f test(x = 1){x}; test() # 1
f test(x = "test".split("")){x}; test() # ["t", "e", "s", "t"]
f test(x = {}){x}; test() # {}
y = 100; f test(x = y){x}; test() # 100
x = 100; f test(x = x){x}; test() # 100
x = 100; f test(x = x){x}; test(1) # 1
Note that mandatory arguments always need to be declared before optional ones:
f(x = null, y){}
# parser errors:
# found mandatory parameter after optional one
# [1:13] f(x = null, y){}
Number ranges (x..y
) now support generating ranges in descending order:
3..1 # [3, 2, 1]
See ya! :wave:
A fresh new major release of ABS: always be shipping! :ship:
This release was originally intended to be out as 1.13.0
, but a backward-incompatible change forced us to shift gears. Don't fear the upgrade as the incompatible changes are extremely easy to address -- and there's only 3 of them.
Let's get rollin'!
:rocket: :rocket: :rocket: this is a big one! :rocket: :rocket: :rocket:
We've started to integrate a standard library within ABS, with modules such as runtime
and cli
to help you build apps with ease. Requiring a module from the standard library is extremely simple:
runtime = require('@runtime')
runtime.version # 2.0.0
Standard modules are required through the @
prefix. To learn more about ABS's standard library visit the documentation.
As part of the initial rollout of the standard library, we've included a @cli
module that helps you building CLI applications. It has a simple API that takes advantage of decorators:
#!/usr/bin/env abs
cli = require('@cli')
@cli.cmd("ip", "finds our IP address", {})
f ip_address(arguments, flags) {
return `curl icanhazip.com`
}
@cli.cmd("date", "Is it Friday already?", {"format": ""})
f date(arguments, flags) {
format = flags.format
return `date ${format}`
}
cli.run()
You can save this script as cli
and make it executable with chmod +x ./cli
. Then you will be able to use the CLI app:
$ ./cli
Available commands:
* date - Is it Friday already?
* help - print this help message
* ip - finds our IP address
$ ./cli help
Available commands:
* date - Is it Friday already?
* help - print this help message
* ip - finds our IP address
$ ./cli ip
87.201.252.69
$ ./cli date
Sat Apr 4 18:06:35 +04 2020
$ ./cli date --format +%s
1586009212
Decorators have gone through a major revamp and are now 100% compatible with Python's ones, which we find extremely powerful:
f uppercase(fn) {
return f() {
return fn(...).upper()
}
}
@uppercase
f stringer(x) {
return x.str()
}
stringer({}) # "{}"
stringer(12) # "12"
stringer("hello") # "HELLO"
The revamp was needed in order to allow any kind of expression to be used as a decorator, and not just plain functions -- for example:
@decorator
@decorator()
@module.decorator()
We have removed the functions slice
and contains
, which were available on both arrays and strings. They have been deprecated for a while and can be easily replaced by the index notation ([1, 2, 3].slice(0, 1)
is equivalent to [1, 2, 3][:1]
) and the in
operator ([1, 2, 3].includes(1)
is equivalent to 1 in [1, 2, 3]
).
We've recapped these 3 backwards-incompatible changes in a simple upgrade guide.
See ya! :wave:
This is a bugfix release that fixes a nasty bug when using both index (a.b
) and property accessors (a['b']
) in assignments, such as a.b.c['d']['e']
(#356).
A new minor release of ABS: always be shipping! :ship:
We've been adding a plethora of functions in this release -- check'em out!
Splits the array into chunks of the given size:
[1, 2, 3].chunk(2) # [[1, 2], [3]]
[1, 2, 3].chunk(10) # [[1,2,3]]
[1, 2, 3].chunk(1.2) # argument to chunk must be a positive integer, got '1.2'
Computes the intersection between 2 arrays:
[1, 2, 3].intersect([]) # []
[1, 2, 3].intersect([3]) # [3]
[1, 2, 3].intersect([3, 1]) # [1, 3]
[1, 2, 3].intersect([1, 2, 3, 4]) # [1, 2, 3]
Computes the difference between 2 arrays, returning elements that are only on the first array:
[1, 2, 3].diff([]) # [1, 2, 3]
[1, 2, 3].diff([3]) # [1, 2]
[1, 2, 3].diff([3, 1]) # [2]
[1, 2, 3].diff([1, 2, 3, 4]) # []
For symmetric difference see diff_symmetric(...)
Computes the symmetric difference between 2 arrays (elements that are only on either of the 2):
[1, 2, 3].diff([]) # [1, 2, 3]
[1, 2, 3].diff([3]) # [1, 2]
[1, 2, 3].diff([3, 1]) # [2]
[1, 2, 3].diff([1, 2, 3, 4]) # [4]
Computes the union between 2 arrays:
[1, 2, 3].union([1, 2, 3, 4]) # [1, 2, 3, 4]
[1, 2, 3].union([3]) # [1, 2, 3]
[].union([3, 1]) # [3, 1]
[1, 2].union([3, 4]) # [1, 2, 3, 4]
Flattens an array a single level deep:
[[1, 2], 3, [4]].flatten([1, 2, 3, 4]) # [1, 2, 3, 4]
[[1, 2, 3, 4]].flatten([1, 2, 3, 4]) # [1, 2, 3, 4]
Flattens an array recursively until no member is an array:
[[[1, 2], [[[[3]]]], [4]]].flatten_deep() # [1, 2, 3, 4]
[[1, [2, 3], 4]].flatten_deep() # [1, 2, 3, 4]
Finds the highest number in an array:
[].max() # NULL
[0, 5, -10, 100].max() # 100
Finds the lowest number in an array:
[].min() # NULL
[0, 5, -10, 100].min() # -10
Reduces the array to a value by iterating through its elements and applying fn
to them:
[1, 2, 3, 4].reduce(f(value, element) { return value + element }, 0) # 10
[1, 2, 3, 4].reduce(f(value, element) { return value + element }, 10) # 20
Partitions the array by applying fn
to all of its elements
and using the result of the function invocation as the key to partition by:
f odd(n) {
return !!(n % 2)
}
[0, 1, 2, 3, 4, 5].partition(odd) # [[0, 2, 4], [1, 3, 5]]
[1, "1", {}].partition(str) # [[1, "1"], [{}]]
Checks whether the number is between min
and max
:
10.between(0, 100) # true
10.between(10, 100) # true
10.between(11, 100) # false
Clamps the number between min and max:
10.clamp(0, 100) # 10
10.clamp(0, 5) # 5
10.clamp(50, 100) # 50
1.5.clamp(2.5, 3) # 2.5
Converts the string to camelCase:
"a short sentence".camel() # aShortSentence
Converts the string to snake_case:
"a short sentence".snake() # a_short_sentence
Converts the string to kebab-case:
"a short sentence".snake() # a-short-sentence
A shorthand syntax supports passing a hash and comparing elements to the given hash:
[null, {"key": "val", "test": 123}].find({"key": "val"}) # {"key": "val", "test": 123}
VERSION
file (#335)This patch release enhances decorators, allowing multiple decorators to be declared over a function (#332):
@debug()
@sleep()
@upper()
f greet(name) {
echo("hello $name")
}
This is a patch release that fixes a nasty scenario with string interpolation (#330). You can now interpolate variables both with $var
and ${var}
so that variables can be embedded within other strings "string${var}string"
(#331).
This is a patch release that upgrades ABS to be built through Go 1.14. Ther are no changes to the language besides the improvements brought by this new release of Go.
This is a bugfix release that fixes a regression introduced in 1.10.1
: early returns from within for
and for in
loops were enabled for all return values, not just explicit ones.
An explicit return is defined as using the return
keyword:
for x in 1..10 {
return x
}
but the early return feature was enabled also for implicit returns:
for x in 1..10 {
x <-- this loop only runs one, and return the first value of x
}
which would make code such as this fail:
list = []
for x in 1..10 {
list.push(x) <-- this will be considered an implicit return, and only run once
}
This was fixed with #325.
Please note that the 1.10.x
branch will not see a backport of this fix.