Functions
Overview¶
Functions provide a powerful way to make your scripts more modular, reusable, and easier to maintain. By defining functions that can be used in both tasks and dialogs, you can write more concise and flexible scripts for managing conversations and computations.Functions can accept parameters, perform calculations or operations, and return a result. These functions can be used in both tasks and dialogs to enhance the flexibility of your scripts.
Basic Syntax¶
A Bubblescript function is defined using the following syntax:
function function_name(_parameter1, _parameter2, ...) do
# function body
# do some work here
return result
end
function_name: The name of the function that will be used to call it.- Parameters: Local variables that are passed to the function. These must be prefixed with an underscore (
_) and are separated by commas. do: Marks the start of the function's body.return: Returns the result of the function.end: Ends the function definition.
Some key points to notice are:
- Functions can return any value, including numbers, strings, or even other variables.
- Functions can be called inside tasks or dialogs to process data or execute specific logic, or from other functions.
- Functions cannot be named the same as any of the builtin functions, e.g. creating a function called
date_formatwill return an error.
Function guards¶
Functions can be defined multiple times with guard clauses. A guard clause allows you to specify conditions under which a particular function definition will be executed. This feature enables more flexible and dynamic handling of different inputs or cases within a function.
A function with a guard clause is defined similarly to a regular function, but with an additional when clause. The when clause specifies the condition that must be true for that particular version of the function to be executed:
function function_name(parameter1, parameter2, ...) when condition do
# function body
end
whenclause: This is where you specify the condition for the function to run.- Condition: Any valid expression that evaluates to a boolean value (
trueorfalse).
If no guard clause condition is met, the default version of the function (the one without a when clause) will be executed.
In the following example, we define a function foo that behaves differently based on the value of the parameter _x.
function foo(_x) when _x < 3 do
return "Input is less than 3"
end
function foo(_x) do
return "Input is 3 or greater"
end
dialog check_input do
say foo(2) # Calls the first version of foo
say foo(5) # Calls the second version of foo
end
In this case, when foo(2) is called, the guard clause _x < 3 is
satisfied, so the first version of foo is executed, returning
"Input is less than 3". When foo(5) is called, the guard clause is
not satisfied, so the second version of foo is executed, returning
"Input is 3 or greater".
Using Functions in Tasks¶
Tasks are used to define operations that happen behind the scenes. Functions can be called within tasks to compute values or trigger processes.
Example 1: Basic Addition¶
function sum(_a, _b) do
return _a + _b
end
task calculate_sum do
result = sum(10, 5) # function call inside a task
end
In the above example:
- A function
sumis defined, which takes two parameters and returns their sum. - Inside the task
calculate_sum, thesumfunction is called with the arguments10and5, and the result is stored in theresultvariable.
Example 2: Concatenating Strings¶
function concatenate(_first, _second) do
return _first + " " + _second
end
task combine_names do
full_name = concatenate("John", "Doe") # combines first and last name
end
In this example:
- The
concatenatefunction joins two strings with a space in between. - The task
combine_namescalls the function to combine the first name "John" and the last name "Doe".
Using Functions in Dialogs¶
Dialogs are used to manage interactions with users. You can call functions inside dialogs to compute values and display them dynamically during conversations.
Example 1: Displaying Calculated Values¶
function multiply(_x, _y) do
return _x * _y
end
dialog main do
say "The result of multiplication is: " + multiply(7, 3)
end
In this example:
- The
multiplyfunction takes two numbers and returns their product. - Inside the dialog
main, themultiplyfunction is called, and the result is displayed in the conversation usingsay.
Example 2: Greeting the User¶
function greet(_name) do
return "Hello, " + _name + "!"
end
dialog welcome_user do
user_name = "Alice"
say greet(user_name)
end
Here:
- The
greetfunction returns a personalized greeting. - Inside the
welcome_userdialog, the function is called to greet the user by name.
Best Practices¶
Some points to consider when writing functions:
- Use Descriptive Function Names: Choose function names that describe what the function does, such as
sum,concatenate, orgreet, to make your code more readable. - Reusability: Use functions to encapsulate repetitive logic and avoid redundancy in your tasks and dialogs.
- Parameter Naming: Always prefix parameters with an underscore (
_) to differentiate them from other variables. - Return Meaningful Values: Ensure that your functions return values that are useful for your tasks or dialogs.
Runtime Type Checking¶
Functions in Bubblescript support runtime type checking through type annotations. This feature helps ensure that functions receive and return values of the expected types, making your scripts more robust and easier to debug.
Runtime type checking causes some performance overhead when used, because it needs to check the types of all arguments and return values at runtime. Use sparingly.
Basic Type Annotations¶
Type annotations can be added to function parameters and return values using the :: syntax:
function get_price(_user :: @schemas.user, _multiplier :: %{type: "number"}) :: %{type: "number"} do
return _user.base_price * _multiplier
end
In this example:
_user :: @schemas.userspecifies that the_userparameter must be a user schema_multiplier :: %{type: "number"}indicates that_multipliermust be a number:: %{type: "number"}after the parameter list specifies that the function must return a number
Combining Types with Guards and Descriptions¶
Type annotations can be combined with guards:
function get_price(_user :: @schemas.user, _multiplier :: %{type: "number"}) :: %{type: "number"},
when _multiplier >= 1 do
return _user.base_price * _multiplier
end
Type checking works across multiple function clauses - each clause's parameters will be checked against their type annotations at runtime.
Available Types¶
Common type annotations include:
- Basic types:
%{type: "string"},%{type: "number"},%{type: "boolean"} - Schema types as defined in
schemasfiles:@schemas.my_user, etc. - Custom schemas:
%{type: "object", properties: {...}}
Type checking happens at runtime, providing immediate feedback if a function receives arguments of incorrect types.
When a typecheck fails, the bot process crashes with a fatal(...) error; which can be caught by an __error__ dialog, when needed.