23 December 2020

Functional shell: a minimal toolbox

functional-shell-minimal-toolbox

I already wrote a post about adopting a functional programming style in Bash scripts. Here I want to explore how to build a minimal, reusable functional toolbox for my bash scripts, avoiding redefinition of base functional bricks whenever I need them.

So, in short: I wish I could write a scripts (say use-functional-bricks.sh) like the following

#!/bin/bash
double () {
  expr $1 '*' 2
}

square () {
  expr $1 '*' $1
}

input=$(seq 1 6)
square_after_double_output=$(map "square" $(map "double" $input))
echo "square_after_double_output $square_after_double_output"

sum() {
	expr $1 '+' $2
}

sum=$(reduce 0 "sum" $input)
echo "The sum is $sum"

referring to “globally” available functions map and reduce(and maybe others, too) without to re-write them everywhere they are needed and without to be bound to external scripts invocation.

The way I think we can solve the problem refers to three interesting features available in bash:

  • export functions from scripts (through export -f)
  • execute scripts in the current shell’s environment, through source command
  • execute scripts when bash starts

So I wrote the following script (say functional-bricks.sh):

#!/bin/bash
map () {
	f=$1
	shift
	for x
	do
		$f $x
	done
}
export -f map

reduce () {
	acc=$1
	f=$2
	shift
	shift
	for curr
	do
		acc=$($f $acc $curr)
	done
	echo $acc
}
export -f reduce

and added the following line at the end of my user’s ~/.bashrc file:

. ~/common/functional-bricks.sh

and… voila!: now map and reduce implemented in functional-bricks.sh are available in all my bash sessions - so I can use them in all my scripts!
And because seeing is beleiving… if I launch the script use-functional-bricks.shdefined above, I get the following output:

square_after_double_output 4
16
36
64
100
144
The sum is 21

No comments:

Post a Comment