Taskle
A concurrent programming library for Gleam that provides Elixir-like Task module functionality. Taskle allows you to run computations asynchronously using lightweight BEAM processes with a simple, type-safe API.
Features
- Simple async/await API inspired by Elixir’s Task module
- Type-safe concurrency with full compile-time guarantees
- Built on BEAM processes for true concurrency and fault tolerance
- Timeout support for all operations
- Task cancellation for resource management
- Parallel processing utilities for lists
- Owner-based access control prevents race conditions
Installation
Add taskle to your Gleam project:
gleam add taskle
Quick Start
import taskle
pub fn main() {
// Create an asynchronous task
let task = taskle.async(fn() {
// Some expensive computation
expensive_computation()
})
// Wait for the result with a timeout (in milliseconds)
case taskle.await(task, 5000) {
Ok(result) -> io.println("Got result: " <> result)
Error(taskle.Timeout) -> io.println("Task timed out")
Error(taskle.Crashed(reason)) -> io.println("Task crashed: " <> reason)
}
}
API
See the module documentation for detailed API reference.
Examples
Basic Usage
import taskle
import gleam/io
import gleam/int
pub fn basic_example() {
let task = taskle.async(fn() {
// Simulate some work
process.sleep(100)
42
})
case taskle.await(task, 1000) {
Ok(result) -> io.println("Result: " <> int.to_string(result))
Error(_) -> io.println("Task failed")
}
}
Parallel Processing
import taskle
import gleam/list
import gleam/int
pub fn parallel_example() {
let numbers = list.range(1, 10)
case taskle.parallel_map(numbers, fn(n) {
// Simulate expensive computation
process.sleep(100)
n * n
}, 5000) {
Ok(squares) -> {
io.println("Squares: " <> string.inspect(squares))
}
Error(err) -> {
io.println("Parallel processing failed")
}
}
}
Task Cancellation
import taskle
pub fn cancellation_example() {
let long_task = taskle.async(fn() {
process.sleep(30_000) // 30 seconds
"finally done"
})
// Cancel after 1 second
process.sleep(1000)
case taskle.cancel(long_task) {
Ok(Nil) -> io.println("Task cancelled successfully")
Error(_) -> io.println("Failed to cancel task")
}
}
Error Handling Example
import taskle
pub fn error_handling_example() {
let risky_task = taskle.async(fn() {
case random_boolean() {
True -> "success"
False -> panic as "something went wrong"
}
})
case taskle.await(risky_task, 5000) {
Ok(result) -> io.println("Success: " <> result)
Error(taskle.Crashed(reason)) -> {
io.println("Task crashed: " <> reason)
}
Error(taskle.Timeout) -> {
io.println("Task took too long")
}
Error(taskle.NotOwner) -> {
io.println("Ownership error")
}
}
}
Best Practices
- Always set appropriate timeouts to prevent hanging operations
- Handle all error cases explicitly for robust applications
- Use
cancel()for cleanup when tasks are no longer needed - Consider
async_unlinked()for fire-and-forget operations - Keep task functions pure when possible for easier testing and reasoning
Performance Considerations
- BEAM processes are lightweight but not free - avoid creating excessive numbers of short-lived tasks
- For CPU-bound work, consider the number of available cores when deciding parallelism level
- Network-bound operations benefit greatly from concurrent execution
- Use
parallel_mapfor batch processing rather than creating tasks manually
Comparison with Other Languages
Taskle provides similar functionality to:
- Elixir:
Task.async/1andTask.await/2 - JavaScript:
Promiseandasync/await - Go: Goroutines with channels
- Rust:
tokio::spawnand.await
The key difference is that Taskle leverages BEAM’s actor model for true parallelism and fault tolerance.
Contributing
Contributions are welcome! Please ensure:
- All tests pass:
gleam test - Code is formatted:
gleam format - New features include tests and documentation
License
This project is licensed under the Apache License 2.0.