Blocks

Blocks are used to scope calculations and definitions to a particular part of the program.

Explicit blocks

As seen previously, parentheses () are used to define explicit start and end points for a block.

let ten = (
	let four = 2 + 2
	four * four - 6
)

Implicit blocks

In some cases, explicit blocks can lead to large amounts of nesting, especially when guarding against some conditions.

fn perform_transaction [.account_id : num, .item_price : num] (
	let account = get_account [.id account_id]
	if !account.exists then throw error [.reason "Account does not exist"]
	if account.balance < item_price then throw error [.reason "Insufficient funds"] 
	else (
		let payment_method = account => get_payment_method
		if !payment_method.exists then throw error [.reason "Account has no payment method"]
		else (
			account => deduct [
				.from payment_method.primary
				.amount item_price
			]
		)
	)
)

To alleviate this, blocks can be implicitly scoped using a colon : instead of parentheses. Implicit blocks run until the end of the first well-defined ancestor block.

fn perform_transaction [.account_id : num, .item_price : num] (
	let account = get_account [.id account_id]
	if !account.exists then error [.reason "Account does not exist"]
	if account.balance < item_price then error [.reason "Insufficient funds"]
	else:

	let payment_method = account => get_payment_method
	if !payment_method.exists then error [.reason "Account has no payment method"]
	else:
	
	account => deduct [
		.from payment_method.primary
		.amount item_price
	]
)

Because implicit blocks run to the end, they aren’t usable if syntax is required after the end of the block.

-- This is not allowed - the `else` is interpreted as part of `then`'s block, which is not valid.
if !payment_method.exists
then: throw error [.reason "Account has no payment method"] 
else: account => deduct [
	.from payment_method.primary
	.amount item_price
]