a while ago, i blogged about uploading files to s3 using curl and provided the solution as two functions written in bash, and basically the only feedback was "wait. you can write functions in bash?"
you can. but in reality, you probably don't want to. the syntax for defining (and calling) bash functions is horrible. there's a reason people genrally don't use them.
writing and using functions in amber, by comparison, is a borderline delight of sanity. if you've ever written a function in php or python or javascript, amber's function syntax will feel familiar and, well, 'normal'.
a simple function to start
let's start with a basic hello-world calibre function:
fun hello() {
echo "hello"
}
hello()
functions are defined with the keyword fun
to keep things nice and terse, and have a body inside of braces. very comortable, standard stuff. when we call this function, the string 'hello' gets printed to STDOUT
.
accepting arguments
arguments can be passed to a function as a comma-separated list. again, no surprises here.
fun personalized_hello(name) {
echo "hello {name}"
}
personalized_hello("gbhorwood")
note here that we're using string interpolation in our echo
statement to output our variable.
return statements
we can return a value from a function using the return
statement, just as we would expect.
fun get_personalized_hello(name) {
return "hello {name}"
}
echo personalized_hello("gbhorwood")
a little bit of type safety
everybody loves some type safety in their programming language, and amber obliges us by accepting optional types for both arguments and return values.
fun sum(a: Num, b: Num): Num {
return a + b
}
types are defined using the colon syntax.
amber has five types:
-
Text
: strings, basically. -
Num
: either integers or floats. -
Bool
: the standardtrue
orfalse
-
Null
: the nothing type. amber usesNull
as a return types for functions that do not return values. -
[<some type>]
: the array type. in amber, arrays cannot contain mixed types, so the type definition for an array includes the type of the array's elements. if we want to define an array of numbers, for instance, we would type it is[Num]
.
important note: if we define one type in a function, we have to define all the types. for instance, we cannot define just the argument type without also defining the return type. not setting a return type of Null
here throws an error.
// this errors because there is no return type
fun say_my_name(name: Text) {
echo name
}
say_my_name("gbhorwood") // WARN Function has typed arguments but a generic return type
we would fix this error by writing our function as:
// this works
fun say_my_name(name: Text): Null {
echo name
}
likewise, if we define the type of one argument, we have to define them all:
// this error because last_name has no type
fun say_my_name(first_name: [Text], last_name): Null {
echo "hello"
}
say_my_name("grant", "horwood") // ERROR Function 'say_my_name' has a mix of generic and typed arguments
and, of course, if we define a type, we have to obey it.
fun say_my_name(name: Text) {
echo name
}
say_my_name(9) // 1st argument 'name' of function 'say_my_name' expects type 'Text', but 'Num' was given
throwing errors with fail
functions in amber can 'throw' an error by using the fail
statement with an exit code. in this example, we want our function to fail if the user is not root.
fun root_only_function() {
unsafe if($whoami$ != "root") {
fail 1
}
echo "only root can do this"
}
in the first installment, we covered handling bash errors using the failed
block. we can 'catch' the errors 'thrown' by fail
the same way.
root_only_function() failed {
echo "failed condition"
}
likewise, we can also ignore the errors we fail
from our functions by using unsafe
.
a while ago, i blogged about uploading files to s3 in bash and provided the solution as two functions written in bash, and basically the only feedback was "wait. you can write functions in bash?"
you can. but in reality, you probably don't want to. the syntax for defining (and calling) bash functions is horrible. there's a reason people genrally don't use them.
writing and using functions in amber, by comparison, is a borderline delight of sanity. if you've ever written a function in php or python or javascript, amber's function syntax will feel familiar and, well, 'normal'.
a simple function to start
let's start with a basic hello-world calibre function:
unsafe root_only_function()
trapping failed
cases in our functions
we can also, of course, handle errors from commands by using the failed
block inside our functions. this function, for example, attempts a shell command and, on failure, throws it's own fail
.
fun failing_touch() {
silent $touch /etc/passwd$ failed {
fail 1
}
}
failing_touch() failed {
echo "function failing_touch failed"
}
note that we applied silent
to our shell command to suppress bash's output. we only want users to see our error messages, not the shell's.
pushing failed
cases up to our function call
trapping an error and throwing an explicit fail
is a bit clumsy. amber also allows us to automatically fail
up to our to where our function is called by replacing the failed
block in our function with ?
.
fun failing_touch() {
silent $touch /etc/passwd$?
}
failing_touch() failed {
echo "function failing_touch failed"
}
in this example, our function, when called, fails exactly the same way as it would if we'd called $touch /etc/passwd$
directly. very handy.
conclusion
this series has covered calling shell commands; handling errors; composing if
statements and writing loops; using the convenience commands in the standard library; and writing functions. is that all amber can do? no. but it is certainly enough for us to start using this language to do useful, meaningful things.
a note about vim
writing code in vim is a joyful thing (or, at least, that's my opinion), but not having syntax highlighting in this modern day and age is intolerable, so i composed an amber syntax file for vim. i've never written a syntax file before and the effort there is clearly sophomoric, but it does work.
🔎 this post was originally written in the grant horwood technical blog
Top comments (1)
Thanks Grant, that was a very useful series of posts, especially for the standard library commands missing from the official docs!