Skip to main content

Loops and Conditions

Paral provides powerful control flow mechanisms through loops and conditions. These features allow you to create dynamic workflows that adapt to data, environment conditions, and runtime states.

Conditional Execution with @if​

The @if directive allows you to conditionally execute tasks based on variable values, comparisons, and logical operations.

Basic Conditional Syntax​

debug_mode = true
environment = "production"

@if debug_mode
task enable_logging {
-> echo "Debug logging enabled"
-> export LOG_LEVEL=debug
}

@if @is(environment, "production")
task production_setup {
-> kubectl apply -f k8s/production/
}

Comparison Functions​

Paral provides comparison functions using @is and @isnot for use with @if:

user_count = 1000
error_rate = 5.2
server_status = "healthy"

@if @is(user_count, "gt", 500)
task scale_up {
-> kubectl scale deployment app --replicas=10
}

@if @is(error_rate, "lt", 1.0)
task reduce_monitoring {
-> @printf("Error rate acceptable: %.2f%%", error_rate)
}

@if @is(server_status, "healthy")
task proceed_deployment {
-> sh scripts/deploy.sh
}

@if @isnot(server_status, "healthy")
task alert_ops_team {
-> slack-cli send "Server unhealthy, deployment blocked" --channel=#ops
}

Available Comparison Functions:

  • @is(a, b) - Equals
  • @isnot(a, b) - Not equals
  • @is(a, "gt", b) - Greater than
  • @is(a, "lt", b) - Less than
  • @is(a, "gte", b) - Greater than or equal
  • @is(a, "lte", b) - Less than or equal

Logical Operations​

is_weekend = false
maintenance_window = true
critical_issue = false

@if @and(is_weekend, maintenance_window)
task scheduled_maintenance {
-> sh scripts/maintenance.sh
}

@if @or(critical_issue, @is(environment, "emergency"))
task emergency_response {
-> python3 scripts/emergency_deploy.py
}

@if @not(critical_issue)
task normal_operations {
-> python3 scripts/routine_tasks.py
}

Logical Functions:

  • @and(condition1, condition2, ...) - All conditions must be true
  • @or(condition1, condition2, ...) - At least one condition must be true
  • @not(condition) - Negates the condition

Complex Conditional Logic​

cpu_usage = 85.5
memory_usage = 70.2
disk_usage = 45.0

@if @and(@is(cpu_usage, "gt", 80), @is(memory_usage, "gt", 75))
task scale_resources {
-> @printf("High resource usage: CPU %.1f%%, Memory %.1f%%", cpu_usage, memory_usage)
-> kubectl patch deployment app -p '{"spec":{"replicas":5}}'
}

@if @or(@is(cpu_usage, "gt", 90), @is(memory_usage, "gt", 90), @is(disk_usage, "gt", 85))
task critical_alert {
-> @printf("Critical resource usage detected")
-> python3 scripts/emergency_scale.py
}

Loops with @for​

The @for directive enables iteration over lists and arrays, executing the task for each item.

Basic Loop Syntax​

servers = ["web1", "web2", "web3"]
ports = [8080, 8081, 8082]

@for servers
task restart_server {
-> @printf("Restarting server: %s", @value)
-> ssh @value "sudo systemctl restart nginx"
}

@for ports
task check_port {
-> @printf("Checking port %d (index %d)", @value, @key)
-> netstat -tuln | grep @value
}

Loop Variables:

  • @value - The current item being processed
  • @key - The current index (0-based position in the list)

Iterating Over Different Data Types​

Simple Lists​

databases = ["users", "products", "analytics", "logs"]

@for databases
task backup_database {
-> @printf("Backing up database %d: %s", @key, @value)
-> pg_dump @value > @sprintf("backup-%s-%s.sql", @value, $(date +%Y%m%d))
-> @stash[@sprintf("backup_%s", @value)] << @sprintf("backup-%s-%s.sql", @value, $(date +%Y%m%d))
}

Numeric Ranges​

worker_count = 5

@for @range(worker_count)
task start_worker {
-> @printf("Starting worker %d", @value)
-> python3 worker.py @sprintf("--id=%d", @value) &
}

File Lists​

@for @glob("*.csv")
task process_csv_file {
-> @printf("Processing file %d: %s", @key, @value)
-> python3 scripts/process_csv.py --input=@value --output=@sprintf("processed_%s", @value)
}

Advanced Loop Patterns​

Parallel Processing with Coordination​

image_files = ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"]

@for image_files
task process_image {
-> @printf("Processing image %d: %s", @key, @value)
-> convert @value -resize 800x600 @sprintf("resized_%s", @value)
-> @buf[@sprintf("processed_%d", @key)] << @sprintf("resized_%s", @value)
}

@wait @eq(@len(@buf), @len(image_files))
task create_gallery {
-> @printf("All %d images processed, creating gallery", @len(image_files))
-> python3 scripts/create_gallery.py --images=@buf
}

Configuration Deployment​

environments = ["dev", "staging", "prod"]
services = ["api", "web", "worker"]

@for environments
task setup_namespace {
-> @printf("Setting up namespace: %s", @value)
-> kubectl create namespace @value
-> @buf[@sprintf("ns_%s", @value)] << @value
}

@for services
@wait @eq(@len(@buf), @len(environments))
task deploy_service_to_all_envs {
-> @printf("Deploying service: %s", @value)
-> @for environments {
kubectl apply -f @sprintf("k8s/%s/%s.yaml", @value, @outer_value) -n @value
}
}

Nested Loops and Conditions​

Combining @for and @if​

servers = ["web1", "web2", "web3", "api1", "api2"]
maintenance_servers = ["web2", "api1"]

@for servers
@if @not(@contains(maintenance_servers, @value))
task health_check_active_servers {
-> @printf("Checking active server: %s", @value)
-> curl -f @sprintf("http://%s:8080/health", @value)
-> @buf[@sprintf("health_%s", @value)] << echo "healthy"
}

Multi-Level Iteration​

regions = ["us-east", "us-west", "eu-central"]
instance_types = ["t3.micro", "t3.small", "t3.medium"]

@for regions
task setup_region {
-> @printf("Setting up region: %s", @value)
-> @stash[@sprintf("region_%d", @key)] << @value

# Nested iteration over instance types
-> @for instance_types {
@printf("Creating instance type %s in region %s", @value, @stash(@sprintf("region_%d", @outer_key))
aws ec2 describe-instance-types --instance-types @value --region @stash(@sprintf("region_%d", @outer_key))
}
}

Real-World Examples​

Database Migration Pipeline​

migration_files = @glob("migrations/*.sql")
target_databases = ["app_db", "analytics_db", "audit_db"]

@for migration_files
task validate_migration {
-> @printf("Validating migration %d: %s", @key, @value)
-> sqlfluff lint @value
-> @buf[@sprintf("migration_%d_valid", @key)] << echo "true"
}

@for target_databases
@wait @eq(@len(@buf), @len(migration_files))
task apply_migrations {
-> @printf("Applying migrations to database: %s", @value)
-> @for migration_files {
@if @eq(@buf(@sprintf("migration_%d_valid", @key)), "true") {
@printf("Applying %s to %s", @value, @outer_value)
psql @outer_value -f @value
}
}
}

Service Health Monitoring​

services = ["web", "api", "database", "redis", "elasticsearch"]
health_threshold = 3

@schedule "*/30 * * * * *" # Every 30 seconds
@for services
task monitor_service_health {
-> @printf("Monitoring service: %s", @value)
-> health_status=$(curl -s -o /dev/null -w "%{http_code}" @sprintf("http://%s:8080/health", @value))
-> @buf[@sprintf("%s_health", @value)] << echo $health_status

-> @if @isnot(@buf(@sprintf("%s_health", @value)), "200") {
@buf[@sprintf("%s_failures", @value)] << @add(@buf(@sprintf("%s_failures", @value)), 1)
}

-> @if @is(@buf(@sprintf("%s_failures", @value)), "gt", health_threshold) {
@buf[@sprintf("%s_alert_needed", @value)] << echo "true"
}
}

@wait @contains(@buf, "_alert_needed")
task send_health_alerts {
-> @for services {
@if @eq(@buf(@sprintf("%s_alert_needed", @value)), "true") {
@printf("ALERT: Service %s has failed health checks", @value)
slack-cli send @sprintf("🚨 Service %s is unhealthy", @value) --channel=#alerts
}
}
}

Feature Flag Deployment​

feature_flags = ["new_ui", "advanced_analytics", "beta_api"]
target_environments = ["staging", "canary", "production"]
rollout_percentage = 10

@for feature_flags
task prepare_feature {
-> @printf("Preparing feature flag: %s", @value)
-> @stash[@sprintf("feature_%s_config", @value)] << cat @sprintf("features/%s.json", @value)
}

@for target_environments
@depend prepare_feature
task deploy_features {
-> @printf("Deploying features to: %s", @value)
-> @for feature_flags {
@if @is(@outer_value, "production") {
@if @is(rollout_percentage, "gte", 50) {
@printf("Deploying %s to production with %d%% rollout", @value, rollout_percentage)
kubectl apply -f @sprintf("features/%s-prod.yaml", @value)
}
} else {
@printf("Deploying %s to %s", @value, @outer_value)
kubectl apply -f @sprintf("features/%s-%s.yaml", @value, @outer_value)
}
}
}

Dynamic Resource Scaling​

load_metrics = [45.2, 78.9, 92.1, 65.4, 38.7]
scale_threshold = 75.0
min_replicas = 2
max_replicas = 20

@for load_metrics
task evaluate_scaling {
-> @printf("Evaluating load metric %d: %.1f%%", @key, @value)

-> @if @is(@value, "gt", scale_threshold) {
current_replicas=$(@buf("current_replicas"))
new_replicas=$(($current_replicas + 2))
@if @is($new_replicas, "lte", max_replicas) {
@printf("Scaling up to %d replicas", $new_replicas)
kubectl scale deployment app --replicas=$new_replicas
@buf["current_replicas"] << echo $new_replicas
}
}

-> @if @and(@is(@value, "lt", 30.0), @is(@buf("current_replicas"), "gt", min_replicas)) {
new_replicas=$((@buf("current_replicas") - 1))
@printf("Scaling down to %d replicas", $new_replicas)
kubectl scale deployment app --replicas=$new_replicas
@buf["current_replicas"] << echo $new_replicas
}
}

Loop Control and Optimization​

Breaking Out of Loops​

servers = ["web1", "web2", "web3", "web4", "web5"]

@for servers
task find_healthy_server {
-> @printf("Testing server: %s", @value)
-> response=$(curl -s -o /dev/null -w "%{http_code}" @sprintf("http://%s:8080/health", @value))

-> @if @is($response, "200") {
@buf["healthy_server"] << @value
@printf("Found healthy server: %s", @value)
@break # Exit the loop early
}
}

Conditional Loop Execution​

backup_files = @glob("backups/*.sql")
restore_needed = true

@if restore_needed
@for backup_files
task restore_from_backup {
-> @printf("Attempting restore from: %s", @value)
-> backup_date=$(echo @value | grep -o '[0-9]\{8\}')

-> @if @is($backup_date, "gt", $(date -d "7 days ago" +%Y%m%d)) {
@printf("Using recent backup: %s", @value)
psql mydb < @value
@buf["restore_completed"] << echo "true"
@break
}
}