🦀 Functional Programming in TuskLang with Rust
Functional Programming in TuskLang with Rust
🧮 Functional Foundation
Functional programming with TuskLang and Rust provides a powerful paradigm for building reliable, testable, and maintainable applications. This guide covers pure functions, immutability, higher-order functions, and functional patterns.
🏗️ Functional Principles
Core Principles
[functional_principles]
pure_functions: true
immutability: true
first_class_functions: true
composition: true[functional_patterns]
map_reduce: true
monad_pattern: true
functor_pattern: true
applicative: true
Functional Concepts
[functional_concepts]
referential_transparency: true
side_effect_free: true
higher_order_functions: true
currying: true
🔧 Pure Functions
Function Purity
[function_purity]
pure_functions: true
side_effects: "avoided"
referential_transparency: true[pure_function_implementation]
// Pure function - same input always produces same output
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Pure function with immutable data
pub fn calculate_total(items: &[Item]) -> f64 {
items.iter()
.map(|item| item.price * item.quantity as f64)
.sum()
}
// Pure function with error handling
pub fn divide(a: f64, b: f64) -> Result<f64, DivisionError> {
if b == 0.0 {
Err(DivisionError::DivisionByZero)
} else {
Ok(a / b)
}
}
// Pure function with validation
pub fn validate_email(email: &str) -> Result<Email, ValidationError> {
if email.contains('@') && email.contains('.') {
Ok(Email(email.to_string()))
} else {
Err(ValidationError::InvalidEmail)
}
}
Immutable Data Structures
[immutable_structures]
immutable_collections: true
persistent_data_structures: true
copy_on_write: true[immutable_implementation]
use std::collections::HashMap;
use std::rc::Rc;
// Immutable user structure
#[derive(Clone, Debug)]
pub struct User {
pub id: String,
pub name: String,
pub email: String,
pub preferences: HashMap<String, String>,
}
impl User {
pub fn new(id: String, name: String, email: String) -> Self {
Self {
id,
name,
email,
preferences: HashMap::new(),
}
}
// Immutable update - returns new instance
pub fn with_preference(mut self, key: String, value: String) -> Self {
self.preferences.insert(key, value);
self
}
// Immutable update with multiple preferences
pub fn with_preferences(mut self, prefs: HashMap<String, String>) -> Self {
self.preferences.extend(prefs);
self
}
// Pure function to get preference
pub fn get_preference(&self, key: &str) -> Option<&String> {
self.preferences.get(key)
}
}
// Immutable configuration
#[derive(Clone, Debug)]
pub struct Config {
pub database_url: String,
pub api_key: String,
pub settings: Rc<HashMap<String, String>>,
}
impl Config {
pub fn new(database_url: String, api_key: String) -> Self {
Self {
database_url,
api_key,
settings: Rc::new(HashMap::new()),
}
}
// Immutable update with new settings
pub fn with_setting(self, key: String, value: String) -> Self {
let mut new_settings = HashMap::clone(&self.settings);
new_settings.insert(key, value);
Self {
settings: Rc::new(new_settings),
..self
}
}
}
🔄 Higher-Order Functions
Function Composition
[function_composition]
composition: true
pipeline: true
currying: true[composition_implementation]
// Function composition
pub fn compose<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(B) -> C,
G: Fn(A) -> B,
{
move |x| f(g(x))
}
// Pipeline operator simulation
pub trait Pipeline {
fn pipe<F, R>(self, f: F) -> R
where
F: FnOnce(Self) -> R;
}
impl<T> Pipeline for T {
fn pipe<F, R>(self, f: F) -> R
where
F: FnOnce(Self) -> R,
{
f(self)
}
}
// Currying
pub fn curry<A, B, C, F>(f: F) -> impl Fn(A) -> impl Fn(B) -> C
where
F: Fn(A, B) -> C,
{
move |a| move |b| f(a, b)
}
// Usage examples
pub fn functional_examples() {
// Function composition
let add_one = |x: i32| x + 1;
let multiply_by_two = |x: i32| x * 2;
let add_one_then_multiply = compose(multiply_by_two, add_one);
assert_eq!(add_one_then_multiply(5), 12);
// Pipeline
let result = 5
.pipe(|x| x + 1)
.pipe(|x| x * 2)
.pipe(|x| x.to_string());
assert_eq!(result, "12");
// Currying
let add = |a: i32, b: i32| a + b;
let add_five = curry(add)(5);
assert_eq!(add_five(3), 8);
}
Map, Filter, Reduce
[map_filter_reduce]
map_operations: true
filter_operations: true
reduce_operations: true[map_filter_reduce_implementation]
// Map operation
pub fn map<T, U, F>(items: &[T], f: F) -> Vec<U>
where
F: Fn(&T) -> U,
{
items.iter().map(f).collect()
}
// Filter operation
pub fn filter<T, F>(items: &[T], predicate: F) -> Vec<T>
where
F: Fn(&T) -> bool,
T: Clone,
{
items.iter().filter(|&x| predicate(x)).cloned().collect()
}
// Reduce operation
pub fn reduce<T, U, F>(items: &[T], initial: U, f: F) -> U
where
F: Fn(U, &T) -> U,
U: Clone,
{
items.iter().fold(initial, f)
}
// Fold operation
pub fn fold<T, U, F>(items: &[T], initial: U, f: F) -> U
where
F: Fn(U, &T) -> U,
U: Clone,
{
items.iter().fold(initial, f)
}
// Usage examples
pub fn collection_operations() {
let numbers = vec![1, 2, 3, 4, 5];
// Map
let doubled = map(&numbers, |x| x * 2);
assert_eq!(doubled, vec![2, 4, 6, 8, 10]);
// Filter
let evens = filter(&numbers, |x| x % 2 == 0);
assert_eq!(evens, vec![2, 4]);
// Reduce
let sum = reduce(&numbers, 0, |acc, x| acc + x);
assert_eq!(sum, 15);
// Fold
let product = fold(&numbers, 1, |acc, x| acc * x);
assert_eq!(product, 120);
}
🔄 Functor Pattern
Functor Implementation
[functor_pattern]
functor_trait: true
option_functor: true
result_functor: true[functor_implementation]
// Functor trait
pub trait Functor<A, B> {
type Output;
fn fmap<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> B;
}
// Option as Functor
impl<A, B> Functor<A, B> for Option<A> {
type Output = Option<B>;
fn fmap<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> B,
{
self.map(f)
}
}
// Result as Functor
impl<A, B, E> Functor<A, B> for Result<A, E> {
type Output = Result<B, E>;
fn fmap<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> B,
{
self.map(f)
}
}
// Vec as Functor
impl<A, B> Functor<A, B> for Vec<A> {
type Output = Vec<B>;
fn fmap<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> B,
{
self.into_iter().map(f).collect()
}
}
// Usage examples
pub fn functor_examples() {
// Option functor
let some_value: Option<i32> = Some(5);
let doubled = some_value.fmap(|x| x * 2);
assert_eq!(doubled, Some(10));
// Result functor
let result: Result<i32, String> = Ok(5);
let doubled = result.fmap(|x| x * 2);
assert_eq!(doubled, Ok(10));
// Vec functor
let numbers = vec![1, 2, 3, 4, 5];
let doubled = numbers.fmap(|x| x * 2);
assert_eq!(doubled, vec![2, 4, 6, 8, 10]);
}
🔄 Monad Pattern
Monad Implementation
[monad_pattern]
monad_trait: true
option_monad: true
result_monad: true
list_monad: true[monad_implementation]
// Monad trait
pub trait Monad<A, B> {
type Output;
fn bind<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> Self::Output;
}
// Option as Monad
impl<A, B> Monad<A, B> for Option<A> {
type Output = Option<B>;
fn bind<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> Option<B>,
{
self.and_then(f)
}
}
// Result as Monad
impl<A, B, E> Monad<A, B> for Result<A, E> {
type Output = Result<B, E>;
fn bind<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> Result<B, E>,
{
self.and_then(f)
}
}
// Vec as Monad
impl<A, B> Monad<A, B> for Vec<A> {
type Output = Vec<B>;
fn bind<F>(self, f: F) -> Self::Output
where
F: Fn(A) -> Vec<B>,
{
self.into_iter().flat_map(f).collect()
}
}
// Monad comprehension
pub fn monad_comprehension() {
// Option monad comprehension
let result = Some(5)
.bind(|x| Some(x + 1))
.bind(|x| Some(x * 2))
.bind(|x| if x > 10 { Some(x) } else { None });
assert_eq!(result, Some(12));
// Result monad comprehension
let result: Result<i32, String> = Ok(5)
.bind(|x| Ok(x + 1))
.bind(|x| Ok(x * 2))
.bind(|x| if x > 10 { Ok(x) } else { Err("Too small".to_string()) });
assert_eq!(result, Ok(12));
// List monad comprehension
let result = vec![1, 2, 3]
.bind(|x| vec![x, x * 2])
.bind(|x| if x % 2 == 0 { vec![x] } else { vec![] });
assert_eq!(result, vec![2, 4, 6]);
}
🔄 Applicative Pattern
Applicative Implementation
[applicative_pattern]
applicative_trait: true
pure_function: true
apply_function: true[applicative_implementation]
// Applicative trait
pub trait Applicative<A, B> {
type Output;
fn pure(value: A) -> Self;
fn apply<F>(self, f: Self) -> Self::Output
where
F: Fn(A) -> B;
}
// Option as Applicative
impl<A, B> Applicative<A, B> for Option<A> {
type Output = Option<B>;
fn pure(value: A) -> Self {
Some(value)
}
fn apply<F>(self, f: Option<F>) -> Self::Output
where
F: Fn(A) -> B,
{
match (self, f) {
(Some(value), Some(func)) => Some(func(value)),
_ => None,
}
}
}
// Result as Applicative
impl<A, B, E> Applicative<A, B> for Result<A, E> {
type Output = Result<B, E>;
fn pure(value: A) -> Self {
Ok(value)
}
fn apply<F>(self, f: Result<F, E>) -> Self::Output
where
F: Fn(A) -> B,
{
match (self, f) {
(Ok(value), Ok(func)) => Ok(func(value)),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e),
}
}
}
// Usage examples
pub fn applicative_examples() {
// Option applicative
let value = Option::pure(5);
let func = Option::pure(|x: i32| x * 2);
let result = value.apply(func);
assert_eq!(result, Some(10));
// Result applicative
let value = Result::<i32, String>::pure(5);
let func = Result::<fn(i32) -> i32, String>::pure(|x| x * 2);
let result = value.apply(func);
assert_eq!(result, Ok(10));
}
🔄 Lens Pattern
Lens Implementation
[lens_pattern]
lens_trait: true
getter_setter: true
composition: true[lens_implementation]
// Lens trait
pub trait Lens<S, A> {
fn get(&self, source: &S) -> A;
fn set(&self, source: S, value: A) -> S;
fn modify<F>(&self, source: S, f: F) -> S
where
F: Fn(A) -> A,
{
let value = self.get(&source);
let new_value = f(value);
self.set(source, new_value)
}
}
// User lens
pub struct UserNameLens;
impl Lens<User, String> for UserNameLens {
fn get(&self, user: &User) -> String {
user.name.clone()
}
fn set(&self, mut user: User, name: String) -> User {
user.name = name;
user
}
}
// Email lens
pub struct UserEmailLens;
impl Lens<User, String> for UserEmailLens {
fn get(&self, user: &User) -> String {
user.email.clone()
}
fn set(&self, mut user: User, email: String) -> User {
user.email = email;
user
}
}
// Lens composition
pub struct ComposeLens<L1, L2> {
outer: L1,
inner: L2,
}
impl<S, A, B, L1, L2> Lens<S, B> for ComposeLens<L1, L2>
where
L1: Lens<S, A>,
L2: Lens<A, B>,
{
fn get(&self, source: &S) -> B {
let intermediate = self.outer.get(source);
self.inner.get(&intermediate)
}
fn set(&self, source: S, value: B) -> S {
let intermediate = self.outer.get(&source);
let new_intermediate = self.inner.set(intermediate, value);
self.outer.set(source, new_intermediate)
}
}
// Usage examples
pub fn lens_examples() {
let user = User::new(
"1".to_string(),
"John Doe".to_string(),
"john@example.com".to_string(),
);
let name_lens = UserNameLens;
let email_lens = UserEmailLens;
// Get value
let name = name_lens.get(&user);
assert_eq!(name, "John Doe");
// Set value
let updated_user = name_lens.set(user, "Jane Doe".to_string());
assert_eq!(updated_user.name, "Jane Doe");
// Modify value
let updated_user = name_lens.modify(updated_user, |name| format!("Dr. {}", name));
assert_eq!(updated_user.name, "Dr. Jane Doe");
}
🔄 Pattern Matching
Advanced Pattern Matching
[pattern_matching]
destructuring: true
guards: true
multiple_patterns: true[pattern_matching_implementation]
// Pattern matching with destructuring
pub fn process_user(user: User) -> String {
match user {
User { name, email, .. } if email.contains("@company.com") => {
format!("Employee: {}", name)
}
User { name, email, .. } if email.contains("@gmail.com") => {
format!("Gmail user: {}", name)
}
User { name, .. } => {
format!("User: {}", name)
}
}
}
// Pattern matching with Result
pub fn process_result(result: Result<i32, String>) -> String {
match result {
Ok(value) if value > 0 => format!("Positive: {}", value),
Ok(0) => "Zero".to_string(),
Ok(value) => format!("Negative: {}", value),
Err(error) => format!("Error: {}", error),
}
}
// Pattern matching with Option
pub fn process_option(option: Option<i32>) -> String {
match option {
Some(value) if value > 10 => format!("Large: {}", value),
Some(value) => format!("Small: {}", value),
None => "No value".to_string(),
}
}
// Pattern matching with tuples
pub fn process_tuple(tuple: (i32, String, bool)) -> String {
match tuple {
(0, name, true) => format!("Active user: {}", name),
(id, name, false) => format!("Inactive user {} with id {}", name, id),
(_, _, _) => "Unknown".to_string(),
}
}
🔄 Functional Data Processing
Data Pipeline
[data_pipeline]
pipeline_operations: true
stream_processing: true
batch_processing: true[pipeline_implementation]
// Functional data pipeline
pub struct Pipeline<T> {
operations: Vec<Box<dyn Fn(Vec<T>) -> Vec<T>>>,
}
impl<T> Pipeline<T> {
pub fn new() -> Self {
Self {
operations: Vec::new(),
}
}
pub fn map<F>(mut self, f: F) -> Self
where
F: Fn(&T) -> T + 'static,
T: Clone,
{
self.operations.push(Box::new(move |items| {
items.iter().map(|item| f(item)).collect()
}));
self
}
pub fn filter<F>(mut self, predicate: F) -> Self
where
F: Fn(&T) -> bool + 'static,
T: Clone,
{
self.operations.push(Box::new(move |items| {
items.into_iter().filter(|item| predicate(item)).collect()
}));
self
}
pub fn sort_by<F>(mut self, compare: F) -> Self
where
F: Fn(&T, &T) -> std::cmp::Ordering + 'static,
T: Clone,
{
self.operations.push(Box::new(move |mut items| {
items.sort_by(&compare);
items
}));
self
}
pub fn execute(self, data: Vec<T>) -> Vec<T> {
self.operations.into_iter().fold(data, |acc, op| op(acc))
}
}
// Usage example
pub fn pipeline_example() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let pipeline = Pipeline::new()
.filter(|&x| x % 2 == 0)
.map(|&x| x * 2)
.sort_by(|a, b| b.cmp(a));
let result = pipeline.execute(numbers);
assert_eq!(result, vec![20, 16, 12, 8, 4]);
}
🎯 Best Practices
1. Function Design
- Write pure functions - Avoid side effects - Use immutable data - Prefer composition over inheritance2. Data Handling
- Use immutable data structures - Implement proper error handling - Use pattern matching - Leverage type safety3. Performance
- Use lazy evaluation - Implement efficient algorithms - Avoid unnecessary allocations - Profile your code4. Testing
- Test pure functions - Use property-based testing - Test edge cases - Verify referential transparency5. Composition
- Compose small functions - Use higher-order functions - Leverage monads for error handling - Use applicatives for parallel operationsFunctional programming with TuskLang and Rust provides a powerful foundation for building reliable, testable, and maintainable applications that are easier to reason about and debug.