diff --git a/Chapter01/Cargo.toml b/Chapter01/Cargo.toml new file mode 100644 index 0000000..3641b63 --- /dev/null +++ b/Chapter01/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "Chapter1" +version = "1.0.0" + +[dependencies] +lazy_static = "1" +cached = "0.5" +metaderive = { path = "metaderive" } + +[[bin]] +name = "intro_binding" +path = "intro_binding.rs" + +[[bin]] +name = "intro_datatypes" +path = "intro_datatypes.rs" + +[[bin]] +name = "intro_expressions" +path = "intro_expressions.rs" + +[[bin]] +name = "intro_functions" +path = "intro_functions.rs" + +[[bin]] +name = "intro_generics" +path = "intro_generics.rs" + +[[bin]] +name = "intro_iterators" +path = "intro_iterators.rs" + +[[bin]] +name = "intro_metaprogramming" +path = "intro_metaprogramming.rs" + +[[bin]] +name = "intro_mixoopfp" +path = "intro_mixoopfp.rs" + +[[bin]] +name = "intro_patterns" +path = "intro_patterns.rs" diff --git a/Chapter01/README.md b/Chapter01/README.md new file mode 100644 index 0000000..d1e615d --- /dev/null +++ b/Chapter01/README.md @@ -0,0 +1,5 @@ +# Chapter 1 – Hands On Functional Programming in RUST +To build all examples from each chapter section, call "cargo build" from the command line. Executables will be created in the target/debug folder. + + + diff --git a/Chapter01/intro_binding.rs b/Chapter01/intro_binding.rs new file mode 100644 index 0000000..7096b98 --- /dev/null +++ b/Chapter01/intro_binding.rs @@ -0,0 +1,71 @@ +use std::thread; +use std::sync::{Mutex, Arc}; +use std::sync::mpsc::channel; + +fn scoped() { + vec![1, 2, 3]; +} + +fn scoped2() -> Vec { + vec![1, 2, 3] +} + +fn scoped3() { + let v1 = vec![1, 2, 3]; + let v2 = v1; + //it is now illegal to reference v1, + //because ownership has been transferred to v2 +} + +fn scoped4() { + vec![1, 2, 3].clone(); + "".to_string().clone(); +} + +fn scoped5() { + fn foo(v1: &Vec){} + let v1 = vec![1, 2, 3]; + foo(&v1); + //v1 is still valid, ownership has been returned + v1; +} + +fn thread1() { + let v = vec![1, 2, 3]; + let handle = thread::spawn(move || { + println!("Here's a vector: {:?}", v); + }); + handle.join(); +} + +fn thread2() +{ + let counter = Arc::new(Mutex::new(0)); + let mut handles = vec![]; + for _ in 0..10 { + let counter = Arc::clone(&counter); + let handle = thread::spawn(move || { + let mut num = counter.lock().unwrap(); + *num += 1; + }); + handles.push(handle); + } + for handle in handles { + handle.join().unwrap(); + } + println!("Result: {}", *counter.lock().unwrap()); +} + +fn thread3() { + let (sender, receiver) = channel(); + let handle = thread::spawn(move || { + //do work + let v = vec![1, 2, 3]; + sender.send(v).unwrap(); + }); + handle.join(); + receiver.recv().unwrap(); +} + +fn main() { +} diff --git a/Chapter01/intro_datatypes.rs b/Chapter01/intro_datatypes.rs new file mode 100644 index 0000000..7b1fb9f --- /dev/null +++ b/Chapter01/intro_datatypes.rs @@ -0,0 +1,66 @@ +//It can be helpful to create shorthand names for complex types +type Name = String; + +//Structs can be repetitive if you just want a way to store multiple values together +struct Data1 +{ + a: i32, + b: f64, + c: String +} +struct Data2 +{ + a: u32, + b: String, + c: f64 +} + +//Tuples help eliminate redundant struct definitions +//no prior type definitions are needed here and the aliases are redundant +type Tuple1 = (i32, f64, String); +type Tuple2 = (u32, String, f64); + +//Standard operators can be implemented with traits +//anyone coming from an ML family language may appreciate +use std::ops::Mul; + +//Standard library collections etc. are generic +use std::collections::HashMap; +type CustomHashMap = HashMap; + +//Tagged Unions can be used to create typesafe definitions of structures that can't be safely described in pure OOP +enum BTree +{ + Branch { val:T, left:Box>, right:Box> }, + Leaf { val:T } +} + +//Commonly, Tagged Unions are used for complex data structures with many possible union options +enum Term +{ + TermVal { value: String }, + TermVar { symbol: String }, + TermApp { f: Box, x: Box }, + TermAbs { arg: String, body: Box } +} + +//Traits are a bit like Object Classes +trait Data1Trait +{ + //Traits can define constructors + fn new(a: i32, b: f64, c: String) -> Self; + + //Traits can have methods, which reference "self" + fn get_a(&self) -> i32; + fn get_b(&self) -> f64; + fn get_c(&self) -> String; +} + +//Traits are also like Data Classes +trait BehaviourOfShow +{ + fn show(&self) -> String; +} + +fn main() { +} diff --git a/Chapter01/intro_expressions.rs b/Chapter01/intro_expressions.rs new file mode 100644 index 0000000..446ccf7 --- /dev/null +++ b/Chapter01/intro_expressions.rs @@ -0,0 +1,51 @@ +struct MyStruct { + a: u32, + b: f32, + c: String +} + +enum Term { + TermVal { value: String }, + TermVar { symbol: String }, + TermApp { f: Box, x: Box }, + TermAbs { arg: String, body: Box } +} + +fn main() { + + let x = { + fn f(x: u32) -> u32 { + x*x + } + let y = f(5); + y*3 + }; + + let x; + if true { + x = 1; + } else { + x = 2; + } + + let x = if true { 1 } else { 2 }; + + MyStruct { + a: 1, + b: 1.0, + c: "".to_string() + }; + + (1, 1.0, "".to_string()); + + let t = Term::TermVar { + symbol: "".to_string() + }; + match t { + Term::TermVal { value: v1 } => v1, + Term::TermVar { symbol: v1 } => v1, + Term::TermApp { f: v1, x: v2 } => "TermApp(?,?)".to_string(), + Term::TermAbs { arg: v1, body: v2 } => "TermAbs(?,?)".to_string() + }; + +} diff --git a/Chapter01/intro_functions.rs b/Chapter01/intro_functions.rs new file mode 100644 index 0000000..38b50f4 --- /dev/null +++ b/Chapter01/intro_functions.rs @@ -0,0 +1,30 @@ +fn main() { + + (0..10).map(|x| x*x); + + (0..10).map(|x| { + + fn f(y: u32) -> u32 { + y*y + } + + let z = f(x+1) * f(x+2); + + z*z + + }); + + fn f(g: T, x: u32) -> u32 + where T: Fn(u32) -> u32 + { + g(x+1) * g(x+2) + } + f(|x|{x*x}, 2); + + (0..10).map(|x| x*x) + .inspect(|x|{ println!("value {}", *x) }) + .filter(|x| *x<3) + .filter_map(|x| Some(x)) + .fold(0, |x, y| x+y); + +} diff --git a/Chapter01/intro_generics.rs b/Chapter01/intro_generics.rs new file mode 100644 index 0000000..d981686 --- /dev/null +++ b/Chapter01/intro_generics.rs @@ -0,0 +1,56 @@ +//Data Type Definitions +struct PointU32 +{ + x: u32, + y: u32 +} +struct PointF32 +{ + x: f32, + y: f32 +} + +//can be written with generics +struct Point +{ + x: T, + y: T +} + +//Function Definitions +fn foo_u32(x: u32) -> u32 +{ + x*x +} +fn foo_f32(x: f32) -> f32 +{ + x*x +} + +//can be written with generics +fn foo(x: T) -> T + where T: std::ops::Mul + Copy +{ + x*x +} + +//even functions can be sent to generics +//we call these "higher order functions" +fn bar(f: F, x: T) -> T + where F: Fn(T) -> T +{ + f(x) +} + +fn main() +{ + PointU32 { x:1, y:1 }; + PointF32 { x:1.0, y:1.0 }; + Point { x:1, y:1 }; + Point { x:1.0, y:1.0 }; + foo_u32(1); + foo_f32(1.0); + foo(1); + foo(1.0); + bar(|x|{x}, 1); +} diff --git a/Chapter01/intro_iterators.rs b/Chapter01/intro_iterators.rs new file mode 100644 index 0000000..9548bb2 --- /dev/null +++ b/Chapter01/intro_iterators.rs @@ -0,0 +1,21 @@ +fn main() { + + (0..10).chain(10..20); + + (0..10).zip(10..20); + + (0..10).enumerate(); + + (0..10).inspect(|x|{ println!("value {}", *x) }); + + (0..10).map(|x| x*x); + + (0..10).filter(|x| *x<3); + + (0..10).fold(0, |x,y| x+y); + + for i in (0..10) {} + + let v: Vec = (0..10).collect(); + +} diff --git a/Chapter01/intro_metaprogramming.rs b/Chapter01/intro_metaprogramming.rs new file mode 100644 index 0000000..b31f144 --- /dev/null +++ b/Chapter01/intro_metaprogramming.rs @@ -0,0 +1,61 @@ +#[macro_use] +extern crate metaderive; + +macro_rules! my_vec_macro { + ( $( $x:expr ),* ) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push($x); + )* + temp_vec + } + }; +} + +macro_rules! my_macro_branch +{ + (1 $e:expr) => (println!("mode 1: {}", $e)); + (2 $e:expr) => (println!("mode 2: {}", $e)); +} + +enum DSLTerm { + TVar { symbol: String }, + TAbs { param: String, body: Box }, + TApp { f: Box, x: Box } +} + +macro_rules! dsl +{ + ( ( $($e:tt)* ) ) => (dsl!( $($e)* )); + ( $e:ident ) => (DSLTerm::TVar { symbol: stringify!($e).to_string() }); + ( fn $p:ident . $b:tt ) => (DSLTerm::TAbs { param: stringify!($p).to_string(), body: Box::new(dsl!($b)) }); + ( $f:tt $x:tt ) => (DSLTerm::TApp { f: Box::new(dsl!($f)), x: Box::new(dsl!($x)) }); +} + +pub trait TypeName { + fn typename() -> String; +} + +#[derive(TypeName)] +struct MyStructA +{ + a: u32, + b: f32 +} + +fn main() +{ + + println!("this is a macro {} {}", 1, 2); + + my_vec_macro!(1, 2, 3); + + my_macro_branch!(1 "abc"); + my_macro_branch!(2 "def"); + + dsl!( a ); + dsl!( fn a . a ); + dsl!( f a ); + dsl!( (f a) ); +} diff --git a/Chapter01/intro_mixoopfp.rs b/Chapter01/intro_mixoopfp.rs new file mode 100644 index 0000000..0937e9f --- /dev/null +++ b/Chapter01/intro_mixoopfp.rs @@ -0,0 +1,51 @@ +struct MyObject +{ + a: u32, + b: f32, + c: String +} + +trait MyObjectTrait +{ + fn new(a: u32, b: f32, c: String) -> Self; + + fn get_a(&self) -> u32; + fn get_b(&self) -> f32; + fn get_c(&self) -> String; +} + +impl MyObjectTrait for MyObject +{ + fn new(a: u32, b: f32, c: String) -> Self + { + MyObject { a: a, b: b, c: c } + } + fn get_a(&self) -> u32 + { + self.a + } + fn get_b(&self) -> f32 + { + self.b + } + fn get_c(&self) -> String + { + self.c.clone() + } +} + +trait MyObjectApply { + fn apply(&self, f: T) -> R + where T: Fn(u32,f32,String) -> R; +} + +impl MyObjectApply for MyObject { + fn apply(&self, f: T) -> R + where T: Fn(u32,f32,String) -> R + { + f(self.a, self.b, self.c.clone()) + } +} + +fn main() { +} diff --git a/Chapter01/intro_patterns.rs b/Chapter01/intro_patterns.rs new file mode 100644 index 0000000..b06204b --- /dev/null +++ b/Chapter01/intro_patterns.rs @@ -0,0 +1,52 @@ +#[macro_use] extern crate cached; +#[macro_use] extern crate lazy_static; + +trait Monad { + fn return_(t: A) -> Self; + //:: A -> Monad + + fn bind(m: Self, f: Fn(A) -> MB) -> MB + where MB: Monad; + //:: Monad -> (A -> Monad)) -> Monad +} + +fn not_curried(p1: u32, p2: u32) -> u32 +{ + p1 + p2 +} + + +fn curried(p1: u32) -> Box u32> +{ + Box::new(move |p2: u32| { + p1 + p2 + }) +} + +cached!{ + FIB; + fn fib(n: u64) -> u64 = { + if n == 0 || n == 1 { return n } + fib(n-1) + fib(n-2) + } +} + +fn main() +{ + let fsin = |x: f64| x.sin(); + let fabs = |x: f64| x.abs(); + let transform = |x: f64| fabs(fsin(x)); + + not_curried(1, 2); + curried(1)(2); + + let immutable_v1 = 1; + //immutable_v1 = 2; //invalid + let mut mutable_v2 = 1; + mutable_v2 = 2; + + let x = { println!("side effect"); 1 + 2 }; + let y = ||{ println!("side effect"); 1 + 2 }; + + fib(30); +} diff --git a/Chapter01/metaderive/Cargo.toml b/Chapter01/metaderive/Cargo.toml new file mode 100644 index 0000000..665d43a --- /dev/null +++ b/Chapter01/metaderive/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "metaderive" +version = "1.0.0" + +[dependencies] +syn = "0.12" +quote = "0.4" + +[lib] +proc-macro = true diff --git a/Chapter01/metaderive/src/lib.rs b/Chapter01/metaderive/src/lib.rs new file mode 100644 index 0000000..7f969fb --- /dev/null +++ b/Chapter01/metaderive/src/lib.rs @@ -0,0 +1,25 @@ +#![crate_type = "proc-macro"] +extern crate proc_macro; +extern crate syn; +#[macro_use] extern crate quote; +use proc_macro::TokenStream; + +#[proc_macro_derive(TypeName)] +pub fn type_name(input: TokenStream) -> TokenStream { + // Parse token stream into input AST + let ast = syn::parse(input).unwrap(); + + // Generate output AST + impl_typename(&ast).into() +} + +fn impl_typename(ast: &syn::DeriveInput) -> quote::Tokens { + let name = &ast.ident; + quote! { + impl TypeName for #name { + fn typename() -> String { + stringify!(#name).to_string() + } + } + } +}