Code Patterns & Best Practices¶
This guide covers common patterns, idioms, and best practices when writing DYMS code. These patterns will help you write more maintainable, efficient, and idiomatic DYMS programs.
Variable Declarations¶
Prefer let
over var
¶
Use let
by default unless you specifically need reassignment:
Use const
for True Constants¶
Reserve const
for compile-time constants that will never change:
Function Patterns¶
Factory Functions for Object Creation¶
Use factory functions to create objects with methods:
funct createUser(name, email) {
let userData = {
"name": name,
"email": email,
"created": time.now()
}
return {
"getName": funct() { return userData.name },
"getEmail": funct() { return userData.email },
"getAge": funct() { return time.now() - userData.created },
"updateEmail": funct(newEmail) {
userData.email = newEmail
return userData.email
}
}
}
// Usage
let user = createUser("Alice", "alice@example.com")
println(user.getName())
user.updateEmail("alice.new@example.com")
Higher-Order Functions¶
Leverage first-class functions for powerful abstractions:
// Generic retry logic
funct withRetry(operation, maxRetries) {
return funct() {
let attempts = 0
while (attempts < maxRetries) {
try {
return operation()
} catch(error) {
++attempts
if (attempts >= maxRetries) {
return "Failed after " + maxRetries + " attempts"
}
println("Attempt", attempts, "failed, retrying...")
}
}
}
}
// Usage
let unreliableTask = funct() {
if (time.now() % 3 == 0) { // Randomly fail
return "Task failed"
}
return "Task succeeded"
}
let robustTask = withRetry(unreliableTask, 3)
println(robustTask())
Partial Application Pattern¶
Create specialized functions from general ones:
funct makeValidator(rule, message) {
return funct(value) {
if (rule(value)) {
return {"valid": true, "value": value}
}
return {"valid": false, "error": message}
}
}
// Create specific validators
let isEmail = makeValidator(
funct(str) { return str.indexOf("@") > 0 },
"Invalid email format"
)
let isPositive = makeValidator(
funct(num) { return num > 0 },
"Must be positive number"
)
// Usage
println(isEmail("alice@example.com")) // Valid
println(isPositive(-5)) // Invalid
Error Handling Patterns¶
Result Pattern¶
Instead of throwing exceptions, return result objects:
funct safeParse(str) {
try {
let num = parseFloat(str) // Hypothetical function
return {"success": true, "value": num}
} catch(error) {
return {"success": false, "error": error}
}
}
funct processNumber(str) {
let result = safeParse(str)
if (result.success) {
return result.value * 2
}
return "Error: " + result.error
}
println(processNumber("42")) // 84
println(processNumber("abc")) // Error: ...
Chain of Responsibility¶
Handle different error types gracefully:
funct createErrorHandler() {
let handlers = []
return {
"register": funct(condition, handler) {
handlers[handlers.length] = {"condition": condition, "handler": handler}
},
"handle": funct(error) {
for range(i, handlers.length) {
let h = handlers[i]
if (h.condition(error)) {
return h.handler(error)
}
}
return "Unhandled error: " + error
}
}
}
let errorHandler = createErrorHandler()
errorHandler.register(
funct(e) { return e.indexOf("network") >= 0 },
funct(e) { return "Network issue - please check connection" }
)
errorHandler.register(
funct(e) { return e.indexOf("timeout") >= 0 },
funct(e) { return "Request timed out - please try again" }
)
println(errorHandler.handle("network connection failed"))
Data Structure Patterns¶
Builder Pattern for Complex Objects¶
Build complex objects step by step:
funct createQueryBuilder() {
let query = {
"table": "",
"fields": [],
"conditions": [],
"limit": 0
}
return {
"from": funct(tableName) {
query.table = tableName
return this // Enable chaining
},
"select": funct(fields) {
query.fields = fields
return this
},
"where": funct(condition) {
query.conditions[query.conditions.length] = condition
return this
},
"limitTo": funct(count) {
query.limit = count
return this
},
"build": funct() {
let sql = "SELECT " + query.fields.join(", ")
sql = sql + " FROM " + query.table
if (query.conditions.length > 0) {
sql = sql + " WHERE " + query.conditions.join(" AND ")
}
if (query.limit > 0) {
sql = sql + " LIMIT " + query.limit
}
return sql
}
}
}
// Usage
let sql = createQueryBuilder()
.from("users")
.select(["name", "email"])
.where("age > 18")
.where("active = true")
.limitTo(10)
.build()
println(sql)
Array Processing Patterns¶
Work with arrays efficiently:
// Map-like function
funct mapArray(array, transform) {
let result = []
for range(i, array.length) {
result[i] = transform(array[i])
}
return result
}
// Filter-like function
funct filterArray(array, predicate) {
let result = []
let resultIndex = 0
for range(i, array.length) {
if (predicate(array[i])) {
result[resultIndex] = array[i]
++resultIndex
}
}
return result
}
// Usage
let numbers = [1, 2, 3, 4, 5]
let doubled = mapArray(numbers, funct(n) { return n * 2 })
let evens = filterArray(numbers, funct(n) { return n % 2 == 0 })
println("Doubled:", doubled) // [2, 4, 6, 8, 10]
println("Evens:", evens) // [2, 4]
Module Patterns¶
Namespace Pattern¶
Organize related functions in objects:
// Create a math utilities namespace
let MathUtils = {
"clamp": funct(value, min, max) {
if (value < min) return min
if (value > max) return max
return value
},
"lerp": funct(start, end, factor) {
return start + (end - start) * factor
},
"distance": funct(x1, y1, x2, y2) {
let dx = x2 - x1
let dy = y2 - y1
return math.sqrt(dx * dx + dy * dy)
}
}
// Usage
println("Clamped:", MathUtils.clamp(15, 0, 10)) // 10
println("Lerp:", MathUtils.lerp(0, 100, 0.5)) // 50
Module Initialization Pattern¶
Set up modules with configuration:
funct createLogger(config) {
let settings = {
"level": config.level || "INFO",
"prefix": config.prefix || "[LOG]",
"timestamp": config.timestamp || true
}
return {
"info": funct(message) {
if (settings.level == "DEBUG" || settings.level == "INFO") {
let output = settings.prefix + " "
if (settings.timestamp) {
output = output + "[" + time.now() + "] "
}
println(output + message)
}
},
"debug": funct(message) {
if (settings.level == "DEBUG") {
println("[DEBUG] " + message)
}
},
"configure": funct(newConfig) {
// Update settings
if (newConfig.level) settings.level = newConfig.level
if (newConfig.prefix) settings.prefix = newConfig.prefix
}
}
}
let logger = createLogger({"level": "DEBUG", "prefix": "[APP]"})
logger.info("Application started")
logger.debug("Debug information")
Performance Patterns¶
Memoization¶
Cache expensive function results:
funct memoize(fn) {
let cache = {}
return funct(arg) {
let key = "" + arg // Convert to string key
if (cache[key] != null) {
return cache[key]
}
let result = fn(arg)
cache[key] = result
return result
}
}
// Expensive function (fibonacci)
let fibonacci = memoize(funct(n) {
if (n <= 1) return n
// This would be slow without memoization
return fibonacci(n - 1) + fibonacci(n - 2)
})
println("Fib(10):", fibonacci(10)) // Fast after first call
Lazy Evaluation¶
Defer expensive computations:
funct createLazy(factory) {
let cached = null
let computed = false
return {
"get": funct() {
if (!computed) {
cached = factory()
computed = true
}
return cached
},
"reset": funct() {
cached = null
computed = false
}
}
}
let expensiveData = createLazy(funct() {
println("Computing expensive data...")
// Simulate expensive computation
let result = []
for range(i, 1000) {
result[i] = math.sqrt(i)
}
return result
})
// Only computed when needed
println("First access:", expensiveData.get().length) // Computes here
println("Second access:", expensiveData.get().length) // Uses cache
Testing Patterns¶
Simple Assertion Framework¶
Build a basic testing framework:
funct createTester(suiteName) {
let tests = []
let passed = 0
let failed = 0
return {
"test": funct(name, testFn) {
tests[tests.length] = {"name": name, "fn": testFn}
},
"run": funct() {
println("Running test suite:", suiteName)
println("=" * 40)
for range(i, tests.length) {
let test = tests[i]
try {
test.fn()
println("✓ " + test.name)
++passed
} catch(error) {
println("✗ " + test.name + " - " + error)
++failed
}
}
println("\nResults:", passed, "passed,", failed, "failed")
}
}
}
let suite = createTester("Math Functions")
suite.test("Addition works", funct() {
if (2 + 2 != 4) {
return "Expected 4, got " + (2 + 2)
}
})
suite.test("Division by zero", funct() {
if (10 / 0 == 0) {
return "Division by zero should not equal 0"
}
})
suite.run()
Style Guidelines¶
Naming Conventions¶
- Functions: Use camelCase:
getUserData()
,calculateTotal()
- Variables: Use camelCase:
userName
,totalCount
- Constants: Use UPPER_CASE:
MAX_RETRIES
,API_KEY
- Factory functions: Prefix with "create":
createUser()
,createLogger()
Code Organization¶
- Group related functions together
- Use consistent indentation (2 or 4 spaces)
- Add comments for complex logic
- Keep functions focused on a single responsibility
- Prefer composition over complex inheritance patterns
Error Messages¶
Write helpful error messages:
// Good - specific and actionable
if (age < 0) {
return "Age must be a positive number, got: " + age
}
// Avoid - vague and unhelpful
if (age < 0) {
return "Invalid age"
}
These patterns will help you write more maintainable and idiomatic DYMS code. Remember, the best pattern is often the simplest one that solves your problem clearly and correctly!