Simplified WSL Syntax
Simplified WSL provides a more concise, pipeline-style syntax for defining workflows. This alternative syntax reduces boilerplate and makes simple workflows more readable.
Overview
While standard WSL uses explicit workflow, state, and transition blocks, Simplified WSL uses a more compact notation with:
- Pipeline operators (
->) for chaining actions - Error binding (
<-) for error handling - Definition statements (
def) for reusable handlers - Inline action chaining without explicit state names
- Optional module declaration - derives name from filename if omitted
Module Declaration
The module keyword is optional in SimplifiedWSL. If omitted:
- When using a
.swslfile, the module name is automatically derived from the filename (without extension) - When parsing without a filename, the default module name is
main
With explicit module:
module payment_flow
action.Call() -> .
Without module (using filename):
// File: payment_flow.swsl
// Module name will be "payment_flow"
action.Call() -> .
Without module (no filename):
// Module name will default to "main"
action.Call() -> .
Basic Syntax
Standard WSL vs Simplified WSL
Standard WSL:
module example
import services/common
const {
msg: "Hello World",
code: 200
}
workflow hello {
start: Speak
state Speak {
action converse/speak.Say(on: "message", v: $constants.msg)
on success -> Respond
on error -> HandleError
}
state Respond {
action services/common/response.ResponseValue(
message: $response.message,
statusCode: $constants.code
)
end ok
}
state HandleError {
action services/common/response.ResponseValue(
message: "error",
statusCode: 500
)
end error
}
}
Simplified WSL:
import services/common
const {
msg: "Hello World",
code: 200
}
// Define error handler first
def services/common/errors.OnAnyError(msg: "error", statusCode: 400) as errorHandler -> services/common/response.ResponseValue(message: $error.message, statusCode: 500) -> .
// Main action flow with error binding
converse/speak.Say(on: "message", v: $constants.msg) as response <- errorHandler -> services/common/response.ResponseValue(message: $response.message, statusCode: $constants.code) -> .
Key Concepts
1. Pipeline Operator (->)
Chain actions together in a linear flow:
action1() -> action2() -> action3() -> .
The dot (.) at the end signifies the end of the pipeline.
2. Error Binding (<-)
Bind an error handler to an action:
mainAction() as result <- errorHandler -> nextAction() -> .
If mainAction() fails, execution goes to errorHandler.
3. Definition Statement (def)
Define reusable action sequences or handlers:
def handlerName(param1: value1, param2: value2) as alias -> action1() -> action2() -> .
Important: The def keyword creates action templates/aliases that are NOT executed as separate workflow states. Instead, they are inlined at the point where they are referenced. This is particularly useful for defining reusable error handlers.
// Define an error handler template
def errors.OnAnyError(msg: "error") as errorHandler -> .
// The workflow starts with mainAction(), NOT errorHandler
// errorHandler is only executed if mainAction() fails
mainAction() <- errorHandler -> nextAction() -> .
In this example:
- The
defcreates a template callederrorHandler - It does NOT run as the first state of the workflow
- When referenced with
<- errorHandler, the error handler action is inlined at that point - The workflow execution begins with
mainAction(), noterrorHandler
4. Action Aliasing (as)
Capture action results for use in subsequent steps:
action() as result -> nextAction(data: $result.value) -> .
5. Nested Structures in Action Arguments
SimplifiedWSL fully supports complex nested structures in action arguments, allowing you to pass hierarchical configuration directly to your actions:
Simple nested object:
payment.Process(
amount: 100,
metadata: {
user: "test@example.com",
timestamp: 1234567890
}
) -> .
Complex nested structure with arrays:
api.Request(
method: "POST",
url: "https://api.example.com/data",
config: {
timeout: 5000,
retries: 3,
headers: [
{ key: "Content-Type", value: "application/json" },
{ key: "Accept", value: "application/json" },
{ key: "Authorization", value: "Bearer token123" }
],
settings: {
cache: true,
validate: false,
limits: [500, 1000, 5000]
}
}
) -> .
Using constants with nested structures:
const {
paymentConfig: {
provider: "stripe",
currency: "USD",
retries: 3
}
}
payment.Process(
amount: 100,
metadata: {
user: "test@example.com",
tags: ["priority", "verified"]
},
config: $constants.paymentConfig
) -> .
Type conversions:
- String literals →
string - Integer numbers →
int64 - Floating-point numbers →
float64 true/false→boolnull→nil- Objects
{}→map[string]interface{} - Arrays
[]→[]interface{}
Workflow Types & Hierarchical Execution
SimplifiedWSL supports workflow type declarations for hierarchical execution. You can declare a workflow as a feature, solution, workflow, or custom type.
Workflow Type Declaration
Declare the workflow type at the module level, optionally with a name:
module payment_processing
// Type only (workflow name defaults to module name)
feature
// Type with explicit name
feature payment_processor
// Other supported types
solution
workflow
microservice // Custom type
Workflow Types
Solution
Top-level orchestration of features and workflows:
module ecommerce
solution checkout_solution
const {
timeout: 5000
}
def errors.Critical(level: "critical") as errorHandler -> .
# Orchestrate features
payment.ProcessFeature() <- errorHandler ->
notification.SendFeature() <- errorHandler -> .
Feature
Mid-level orchestration of workflows:
module payment
feature payment_processor
const {
retries: 3
}
def errors.Log() as errorHandler -> .
# Orchestrate workflows
payment.Validate() <- errorHandler ->
payment.Process(retries: $constants.retries) <- errorHandler -> .
Workflow
Low-level execution of actions (default):
module validation
workflow validate_payment
def errors.OnError() as errorHandler -> .
payment.CheckAmount() <- errorHandler ->
payment.CheckBalance() <- errorHandler -> .
Context Sharing
All components in the hierarchy share the same WorkerSessionContext:
# In solution: set values
context.Set(key: "user_id", value: "12345") ->
context.Set(key: "amount", value: 100.00) ->
# Call feature
payment.ProcessFeature() -> .
# In feature/workflow: access values
payment.Process(
userId: $context.user_id,
amount: $context.amount
) -> .
Hierarchical Call Patterns
# Solution can call features and workflows
payment.ProcessFeature() ->
notification.SendWorkflow() ->
services/common.Response() -> .
# Feature can call workflows
validate.ValidatePayment() ->
process.ProcessPayment() -> .
# Workflow calls actions only
payment.CheckAmount() ->
payment.Process() -> .
For more details, see the Hierarchical Execution Guide.
Complete Examples
Example 1: Simple API Call
Simplified WSL:
import services/http
const {
apiUrl: "https://api.example.com/users",
timeout: 5000
}
// Define error handler
def http/errors.HandleError() as errorHandler -> response/Error(code: 500) -> .
// Main flow
http/client.Get(url: $constants.apiUrl, timeout: $constants.timeout) as apiResponse <- errorHandler -> response/Success(data: $apiResponse.body) -> .
Example 2: Data Processing Pipeline
import data/processor
import data/validator
const {
batchSize: 100,
maxRetries: 3
}
// Define validation error handler
def validator.HandleInvalid(message: "Validation failed") as validationError -> response/Error(code: 400, message: $error.details) -> .
// Define processing error handler
def processor.HandleProcessingError(retries: $constants.maxRetries) as processingError -> response/Error(code: 500, message: "Processing failed") -> .
// Main pipeline
data/input.Fetch() as rawData <- validationError ->
validator.Validate(data: $rawData) as validData <- processingError ->
processor.Process(data: $validData, batchSize: $constants.batchSize) as result ->
response/Success(processed: $result.count) -> .
Example 3: Multi-Step Transformation
import transform/csv
import transform/json
import storage/s3
const {
bucket: "my-bucket",
format: "json"
}
// Define handlers
def errors.OnTransformError() as transformError -> response/Error(code: 500, message: "Transform failed") -> .
def errors.OnStorageError() as storageError -> response/Error(code: 500, message: "Storage failed") -> .
// Pipeline with multiple error handlers
csv.Read(path: "input.csv") as csvData <- transformError ->
transform/json.Convert(data: $csvData) as jsonData <- storageError ->
storage/s3.Upload(bucket: $constants.bucket, data: $jsonData) as uploadResult ->
response/Success(location: $uploadResult.url) -> .
Example 4: Conditional Processing
import processor
import validator
const {
threshold: 100
}
// Main flow with inline conditional
validator.Check(data: $input) as checkResult <- errorHandler ->
processor.Route(
data: $checkResult,
condition: $checkResult.size > $constants.threshold,
onTrue: processor/parallel.Process,
onFalse: processor/single.Process
) as result ->
response/Success(processed: $result) -> .
Syntax Reference
Operators
| Operator | Description | Example |
|---|---|---|
-> | Pipeline/chain actions | action1() -> action2() |
<- | Bind error handler | action() <- errorHandler |
as | Alias action result | action() as result |
. | End pipeline | action() -> . |
| ` | ` | Type cast |
Keywords
| Keyword | Description | Example |
|---|---|---|
def | Define reusable handler | def handler() as name -> ... |
import | Import modules | import services/common |
const | Define constants | const { key: value } |
Structure
// Imports
import module/path
// Constants
const {
key: value
}
// Definitions
def handlerAction() as handlerName -> action() -> .
// Main pipeline
action1() as result <- errorHandler ->
action2(data: $result) ->
action3() -> .
Advanced Features
Nested Pipelines
// Outer pipeline
action1() as data ->
(
// Nested pipeline
transform1($data) ->
transform2() ->
transform3()
) as transformed ->
action2(result: $transformed) -> .
Multiple Error Handlers
// Different error handlers for different stages
action1() as step1 <- errorHandler1 ->
action2(data: $step1) as step2 <- errorHandler2 ->
action3(data: $step2) <- errorHandler3 -> .
Handler Composition
// Define composed handlers
def baseHandler() as base -> logError() -> .
def extendedHandler() as extended <- base -> notifyAdmin() -> .
// Use in pipeline
mainAction() <- extendedHandler -> nextAction() -> .
Type Casting
Use the pipe operator for type conversions:
const {
count: "42",
price: "19.99"
}
// Type casting in pipelines
validator.Check(count: $constants.count|int, price: $constants.price|float) ->
processor.Process() -> .
Best Practices
1. Use for Simple Workflows
Simplified syntax is ideal for straightforward, linear workflows:
// Good use case
fetch() -> transform() -> validate() -> save() -> .
2. Define Reusable Handlers
Extract common error handling:
def errors.StandardError() as stdError ->
logger.Log(level: "error") ->
response/Error(code: 500) -> .
3. Keep Pipelines Readable
Break long pipelines into multiple lines:
action1() as result1 ->
action2(data: $result1) as result2 ->
action3(data: $result2) as result3 ->
action4(data: $result3) -> .
4. Use Meaningful Aliases
Choose descriptive names for captured results:
// Good
user/fetch.GetUser() as currentUser ->
profile/load.LoadProfile(userId: $currentUser.id) as userProfile ->
// Avoid
user/fetch.GetUser() as x ->
profile/load.LoadProfile(userId: $x.id) as y ->
5. Group Related Definitions
Organize error handlers and utilities together:
// Error handlers
def errors.ValidationError() as validationErr -> ...
def errors.ProcessingError() as processingErr -> ...
def errors.StorageError() as storageErr -> ...
// Main pipeline
...
When to Use Simplified WSL
Good Use Cases
✅ Simple linear workflows - Straight-through processing
✅ Data transformation pipelines - ETL-style operations
✅ API integrations - Fetch, transform, respond
✅ Quick prototypes - Rapid workflow development
When to Use Standard WSL
❌ Complex branching logic - Multiple conditional paths
❌ State machines - Workflows with many states and transitions
❌ Long-running processes - Workflows with extensive orchestration
❌ Team projects - When explicit structure aids collaboration
Comparison Table
| Feature | Standard WSL | Simplified WSL |
|---|---|---|
| Syntax | Verbose, explicit | Concise, pipeline-style |
| States | Named states | Implicit/inline |
| Transitions | Explicit on success/error | Pipeline operators |
| Error Handling | Per-state | Bound handlers |
| Readability | More explicit | More compact |
| Best For | Complex workflows | Simple pipelines |
Migration Between Syntaxes
You can mix both syntaxes in a project. Convert workflows based on complexity:
From Simplified to Standard
When a workflow grows complex, convert to standard WSL:
// Simplified (getting complex)
action1() <- err1 -> action2() <- err2 -> action3() <- err3 -> .
// Better as Standard WSL
workflow complex_flow {
start: Action1
state Action1 {
action action1()
on success -> Action2
on error -> Error1
}
// ... clearer structure
}
Editor Support
.swsl files are first-class in both official editor plugins, with dedicated grammars, snippets for def, feature, solution, hierarchical calls (workflow:, feature:, solution:), and the pipeline operators (->, <-, .). See IDE Plugins for installation.
Related Features
- WSL Overview - Standard WSL syntax
- WSL Specification - Complete grammar
- Conditional Flows - Advanced flow control
- Orchestration - Complex workflow patterns
- IDE Plugins - JetBrains & VS Code support