Functions and Subroutines

Functions and subroutines help define logic in one place, and allow you to use that logic in many other places throughout your rules.By placing your logic into a function or subroutine, you can keep your code "DRY" by following the best practice of "Don't Repeat Yourself".  As in other programming languages, Snap functions and subroutines can both accept input parameters, and functions can return an output value.


Function Example 1

The function is defined in the definition statement in the Start block. From there it can be used in the code.

Function Example 2

Certain functions -- such as those who make database queries, web service calls, or refer to safe functions – cannot be called using the same "call function" block as in Example 1. Instead, they must be called using this similar-looking "call function... returns" block. If you do not see your function in the drop-down list provided by the call function block shown in Example 1, try finding it in this block instead shown in Example 2.

Subroutine Example 3

Subroutines are the same as functions, except they do not return a value. Instead, they perform actions. In this example, it processes a car object such that the car's price attribute is rounded to a whole number.

Communication Between Your Code and a Subroutine / Function

Storing your Subroutine / Function

The Snap language supports both primitive types (expressed like strings or numbers) and complex types, usually expressed as objects.  

  • Primitive types are passed by value.
    In example 1, the function gets a separate, independent copy of the basic data values of 1 and 2.  These values, along with any other variables declared in the function, disappear at the end of the function's life.

  • Objects are passed by reference.
    In example 3, however, "car" passed into the subroutine is an object, since it was created from a database query.  We see the car object has a price property, and may have many other properties as well.  Objects are not passed by value, like primitive strings or numbers.  Objects are passed by reference, meaning the entire original object is passed into the function or subroutine, not an independent copy of it.  Because of this, changes made to that object by the function/subroutine remain, even after the function ends.

When writing a function or subroutine in Snap, consider which location for your library would be best.

Benefit Safe
Function
Global
Rule
Global Event
Rule
Logic available to entire environment

   
Logic available through the REST API

   
Logic hidden

   
Logic available to only this configurator/scene  

Calculates faster, without network lag  

Can write directly to UI elements  

Can read directly from UI elements    

 

 

Epicor CPQ has different places where you can store reusable logic.  Which one is best?  While every application is different, consider these guidelines:

What does the function do? From where is it called? The best location for this function
Snap code that can run locally From only one place,
in only one configurator.

Define your function in that same rule type, so it's easy to maintain.

For example, a function to help a value rule work can be defined at the top of that value rule, or in any preceding value rule.

Snap code that can run locally From many places
in only one configurator.
Define your function in a "global" rule type, so it's visible throughout the configurator.
Snap code that can run locally From many configurators.

For faster user experience:
If performance needs to be fast, then define it as a global rule in a configurator, then copy/paste into other configurators.  For example, name the global rule "Useful Function Library v1.0".  In exchange for speed, you assume some responsibility as an administrator to ensure any updates to the function library in one configurator are distributed across the configurators.  Editing the name of the rule (to "v1.1", for example) can help highlight differences.  

For easier administrator experience:
If the risk of your code forking between two or more configurators is too great, then define your logic as a safe function.  You'll know every configurator is using this same function, and your code exists only once.  In exchange for this administrator convenience, your users will now have slightly slower performance as the safe function (stored on the server) is queried and internet latency will be part of their experience.

Snap code that can only be performed on a server, such as 

  • querying a database
  • sending emails
  • calling a web service
Anywhere
Define it as a safe function.

Remember, safe functions can be called from anywhere.  So if your configurator needs information from a database, simply query the database from a safe function, and then call that safe function from your configurator.  Here's a walkthrough example.

Keep your application secure.
Remember the Snap rules you write for your configurator or scene are executed in one of two places: Server-side or local.  Consider the best location for your code to balance needs for security and speed.

  • Server-Side rules
    These rules are processed on the server: the Snap code is never sent to the user's workstation.  For example, Safe functions are server-side, and the best place to write any proprietary calculations or patented, sensitive lookups.  Your customer only sees the result of the safe function, not how it was generated. 
  • Local rules
    These rules are processed on the user's workstation, tablet, or mobile phone.  Because the are local to the user, they are very fast and responsive.  However, it is possible for some users to look at the code within the rule.  Local rules are the best place for code which runs often, needs to be fast, and does not contain sensitive calculations.

Learn more about where Snap rules are executed

Was this article helpful?