Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonShin committed May 29, 2019
0 parents commit 18485a4
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
/target
**/*.rs.bk
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
@@ -0,0 +1,8 @@
[package]
name = "functional-programming-jargon"
version = "0.1.0"
authors = ["Jason Shin <visualbbasic@gmail.com>"]
edition = "2018"

[dependencies]
partial_application = "0.1.0"
149 changes: 149 additions & 0 deletions README.md
@@ -0,0 +1,149 @@
# Functional Programming Jargon in Rust

Functional programming (FP) provides many advantages, and its popularity has been increasing as a result.
However, each programming paradigm comes with its own unique jargon and FP is no exception. By providing a glossary,
we hope to make learning FP easier.

Where applicable, this document uses terms defined in the [Fantasy Land spec](https://github.com/fantasyland/fantasy-land

The goal of this project includes understanding Rust's capability of programming in functional paradigm.

__Table of Contents__
<!-- RM(noparent,notop) -->

## Arity

The number of arguments a function takes. From words like unary, binary, ternary, etc.
This word has the distinction of being composed of two suffixes, "-ary" and "-ity."
Addition, for example, takes two arguments, and so it is defined as a binary function or a function with an arity of two.
Such a function may sometimes be called "dyadic" by people who prefer Greek roots to Latin.
Likewise, a function that takes a variable number of arguments is called "variadic,"
whereas a binary function must be given two and only two arguments, currying and partial application notwithstanding (see below).

```rust
let sum = |a: i32, b: i32| { a + b }; // The arity of sum is 2
```

## Higher-Order Functions (HOF)

A function which takes a function as an argument and/or returns a function.

```rust
let filter = | predicate: fn(&i32) -> bool, xs: Vec<i32> | {
return xs.into_iter().filter(predicate).collect::<Vec<i32>>();
};
```

```rust
let is_even = |x: &i32| { x % 2 == 0 };
```

```rust
filter(is_even, vec![1, 2, 3, 4, 5, 6]);
```

## Closure

A closure is a scope which retains variables available to a function when it's created. This is important for
[partial application](#partial-application) to work.

```rust
let add_to = | x: i32 | { move | y: i32 | { x + y } };


```

We can call `add_to` with a number and get back a function with a baked-in `x`. Notice that we also need to move the
ownership of the y to the internal lambda.

```rust
let add_to_five = add_to(5);
```

In this case the `x` is retained in `add_to_five`'s closure with the value `5`. We can then call `add_to_five` with the `y`
and get back the desired number.

```rust
add_to_five(3); // => 8
```

Closures are commonly used in event handlers so that they still have access to variables defined in their parents when they
are eventually called.

__Further reading__
* [Lambda Vs Closure](http://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda)
* [How do JavaScript Closures Work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work)

## Partial Application

Partially applying a function means creating a new function by pre-filling some of the arguments to the original function.

To achieve this easily, we will be using a [partial application crate](https://crates.io/crates/partial_application)

```rust
#[macro_use]
extern crate partial_application;

fn foo(a: i32, b: i32, c: i32, d: i32, mul: i32, off: i32) -> i32 {
(a + b*b + c.pow(3) + d.pow(4)) * mul - off
}

let bar = partial!( foo(_, _, 10, 42, 10, 10) );

assert_eq!(
foo(15, 15, 10, 42, 10, 10),
bar(15, 15)
); // passes
```

Partial application helps create simpler functions from more complex ones by baking in data when you have it.
Curried functions are automatically partially applied.

__Further reading__
* [Partial Application in Haskell](https://wiki.haskell.org/Partial_application)


## Currying

The process of converting a function that takes multiple arguments into a function that takes them one at a time.

Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.


```rust
fn add(x: i32) -> impl Fn(i32)-> i32 {
return move |y| x + y;
}

let add5 = add(5);
add5(10); // 15
```

__Further reading__
* [Currying in Rust](https://hashnode.com/post/currying-in-rust-cjpfb0i2z00cm56s2aideuo4z)

## Purity

A function is pure if the return value is only determined by its input values, and does not produce side effects.

```rust
let greet = |name: &str| { format!("Hi! {}", name) };

greet("Jason"); // Hi! Jason
```

As opposed to each of the following:

```rust
let name = "Jason";

let greet = || -> String {
format!("Hi! {}", name)
};
```

The above example's output is based on data stored outside of the function...

```rust

```
6 changes: 6 additions & 0 deletions src/arity_example.rs
@@ -0,0 +1,6 @@
#[test]
fn arity() {
let sum = |a: i32, b: i32| { a + b };
let result = sum(1, 2);
assert_eq!(result, 3);
}
8 changes: 8 additions & 0 deletions src/closure_example.rs
@@ -0,0 +1,8 @@
#[test]
fn closure() {
let add_to = | x: i32 | { move | y: i32 | { x + y } };

let add_to_five = add_to(5);

assert_eq!(add_to_five(3), 8);
}
10 changes: 10 additions & 0 deletions src/currying_example.rs
@@ -0,0 +1,10 @@
#[test]
fn currying() {
fn add(x: i32) -> impl Fn(i32)-> i32 {
return move |y| x + y;
}

let add5 = add(5);
let result = add5(10);
assert_eq!(result, 15);
}
13 changes: 13 additions & 0 deletions src/hof_example.rs
@@ -0,0 +1,13 @@
#[test]
fn hof() {
let filter = | predicate: fn(&i32) -> bool, xs: Vec<i32> | {
// A good Reddit post on how Filter works https://www.reddit.com/r/rust/comments/3bmua6/can_someone_help_me_understand_stditerfilter/
return xs.into_iter().filter(predicate).collect::<Vec<i32>>();
};

let is_even = |x: &i32| { x % 2 == 0 };

let result = filter(is_even, vec![1, 2, 3, 4, 5, 6]);

assert_eq!(result, vec![2, 4, 6]);
}
13 changes: 13 additions & 0 deletions src/main.rs
@@ -0,0 +1,13 @@
#[macro_use]
extern crate partial_application;

mod arity_example;
mod hof_example;
mod closure_example;
mod partial_application_example;
mod currying_example;
mod purity_example;

fn main() {
println!("Hello, world!");
}
13 changes: 13 additions & 0 deletions src/partial_application_example.rs
@@ -0,0 +1,13 @@
#[test]
fn partial_application() {
fn foo(a: i32, b: i32, c: i32, d: i32, mul: i32, off: i32) -> i32 {
(a + b*b + c.pow(3) + d.pow(4)) * mul - off
}

let bar = partial!(foo(_, _, 10, 42, 10, 10));

assert_eq!(
foo(15, 15, 10, 42, 10, 10),
bar(15, 15)
);
}
22 changes: 22 additions & 0 deletions src/purity_example.rs
@@ -0,0 +1,22 @@
#[test]
fn purity() {
let greet = |name: &str| { format!("Hi! {}", name) };

assert_eq!("Hi! Jason", greet("Jason"));
}

#[test]
fn impure() {
let name = "Jason";

let greet = || -> String {
format!("Hi! {}", name)
};

assert_eq!("Hi! Jason", greet());
}

#[test]
fn impure2() {
let mut greeting = h
}

0 comments on commit 18485a4

Please sign in to comment.