DEV Community

Cover image for amber: writing bash scripts in amber instead. pt. 4: functions
grant horwood
grant horwood

Posted on

amber: writing bash scripts in amber instead. pt. 4: functions

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'.

community disaster meme
a look at the syntax of functions in bash

a simple function to start

let's start with a basic hello-world calibre function:

fun hello() {
    echo "hello"
}

hello()
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

types are defined using the colon syntax.

amber has five types:

  • Text: strings, basically.
  • Num: either integers or floats.
  • Bool: the standard true or false
  • Null: the nothing type. amber uses Null 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
Enter fullscreen mode Exit fullscreen mode

we would fix this error by writing our function as:

// this works
fun say_my_name(name: Text): Null {
    echo name
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
huw_walters_5c40b648f60e4 profile image
Huw Walters

Thanks Grant, that was a very useful series of posts, especially for the standard library commands missing from the official docs!