Skip to content

Commit d020d6d

Browse files
authored
Merge pull request #76 from JasonShin/revamp
Update project
2 parents 4564c42 + f0f4a41 commit d020d6d

17 files changed

Lines changed: 703 additions & 32 deletions

.github/workflows/rust.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
env:
10+
RUST_BACKTRACE: 1
11+
CARGO_TERM_COLOR: always
12+
13+
jobs:
14+
fmt:
15+
name: Format Check
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: dtolnay/rust-toolchain@stable
20+
with:
21+
components: rustfmt
22+
- name: Check formatting
23+
run: cargo fmt --all -- --check
24+
25+
clippy:
26+
name: Clippy (Lint)
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
- uses: dtolnay/rust-toolchain@stable
31+
with:
32+
components: clippy
33+
- name: Run clippy
34+
run: cargo clippy --all-targets --all-features -- -D warnings
35+
# -D warnings makes lint warnings fail CI (fixes Issue #34)
36+
37+
test:
38+
name: Test Suite
39+
runs-on: ${{ matrix.os }}
40+
strategy:
41+
matrix:
42+
os: [ubuntu-latest, macos-latest, windows-latest]
43+
steps:
44+
- uses: actions/checkout@v4
45+
- uses: dtolnay/rust-toolchain@stable
46+
- name: Run tests
47+
run: cargo test --all-features --workspace
48+
- name: Run doc tests
49+
run: cargo test --doc --workspace

.travis.yml

Lines changed: 0 additions & 8 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,47 @@ NOTE: Be sure to merge the latest from "upstream" before making a pull request!
2727

2828
## Development Environments
2929

30-
| type | version |
31-
| ------ | ---------------------- |
32-
| nodejs | Greater than 8.11.0 |
33-
| OS | Linux, Windows and Mac |
30+
| type | version |
31+
| ---- | ---------------------- |
32+
| OS | Linux, Windows and Mac |
3433

35-
You will need to install Rust to work on this project. Installation instruction can be found https://www.rust-lang.org/tools/install.
34+
You will need to install Rust to work on this project. Installation instruction can be found at https://www.rust-lang.org/tools/install.
3635

37-
I have used following versions of Rust and its dependencies while developing
36+
### Requirements
3837

39-
| type | version |
40-
| ------ | ------------------------------------------- |
41-
| rustc | rustc 1.36.0-nightly (7158ed9cb 2019-05-15) |
42-
| cargo | cargo 1.36.0-nightly (759b6161a 2019-05-06) |
38+
- **Rust stable** (1.70.0 or later recommended)
39+
- **No nightly features required** - this project uses only stable Rust features
4340

44-
Also I've enabled nightly mode to use some of the latest features of Rust, follow this instruction: https://github.com/rust-lang/rustup.rs#working-with-nightly-rust
41+
The project automatically uses the correct Rust version via `rust-toolchain.toml`.
42+
43+
### Running Tests
44+
45+
```bash
46+
# Run all tests
47+
cargo test --workspace
48+
49+
# Run tests for a specific package
50+
cargo test -p fp-core
51+
52+
# Run with output
53+
cargo test -- --nocapture
54+
```
55+
56+
### Code Quality Checks
57+
58+
```bash
59+
# Format code
60+
cargo fmt --all
61+
62+
# Check formatting
63+
cargo fmt --all -- --check
64+
65+
# Run clippy lints
66+
cargo clippy --all-targets --all-features
67+
68+
# Fix clippy warnings automatically (where possible)
69+
cargo clippy --all-targets --all-features --fix
70+
```
4571

4672
## Copyright and Licensing
4773

@@ -62,7 +88,7 @@ You can copy and paste the MIT license summary from below.
6288
```
6389
MIT License
6490
65-
Copyright (c) 2019 Jason Shin
91+
Copyright (c) 2026 Jason Shin
6692
6793
Permission is hereby granted, free of charge, to any person obtaining a copy
6894
of this software and associated documentation files (the "Software"), to deal

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2019 Jason Shin
1+
Copyright (c) 2026 Jason Shin
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to deal

fp-core/src/apply.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::functor::Functor;
22
use crate::hkt::HKT;
33

4-
type Applicator<B, S: HKT<B>> = <S as HKT<Box<dyn Fn(<S as HKT<B>>::Current) -> B>>>::Target;
4+
type Applicator<B, S> = <S as HKT<Box<dyn Fn(<S as HKT<B>>::Current) -> B>>>::Target;
55

66
pub trait Apply<B>: Functor<B> + HKT<Box<dyn Fn(<Self as HKT<B>>::Current) -> B>> {
77
fn ap(self, f: Applicator<B, Self>) -> <Self as HKT<B>>::Target;

fp-core/src/lens.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub trait Lens<S, A> {
22
fn over(s: &S, f: &dyn Fn(Option<&A>) -> A) -> S {
33
let result: A = f(Self::get(s));
4-
Self::set(result, &s)
4+
Self::set(result, s)
55
}
66
fn get(s: &S) -> Option<&A>;
77
fn set(a: A, s: &S) -> S;

fp-examples/src/applicative_example.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,97 @@ mod example {
1414
let x = Result::<_, ()>::of(1).ap(Ok(Box::new(|x| x + 1)));
1515
assert_eq!(x, Ok(2));
1616
}
17+
18+
// Additional comprehensive applicative tests
19+
#[test]
20+
fn test_option_pure() {
21+
let result = Option::of(5);
22+
assert_eq!(result, Some(5));
23+
}
24+
25+
#[test]
26+
fn test_result_pure() {
27+
let result: Result<i32, String> = Result::of(5);
28+
assert_eq!(result, Ok(5));
29+
}
30+
31+
#[test]
32+
fn test_option_apply() {
33+
let value = Some(5);
34+
let func = Some(Box::new(|x: i32| x * 2) as Box<dyn Fn(i32) -> i32>);
35+
let result = value.ap(func);
36+
assert_eq!(result, Some(10));
37+
}
38+
39+
#[test]
40+
fn test_option_apply_none_value() {
41+
let value: Option<i32> = None;
42+
let func = Some(Box::new(|x: i32| x * 2) as Box<dyn Fn(i32) -> i32>);
43+
let result = value.ap(func);
44+
assert_eq!(result, None);
45+
}
46+
47+
#[test]
48+
fn test_option_apply_none_func() {
49+
let value = Some(5);
50+
let func: Option<Box<dyn Fn(i32) -> i32>> = None;
51+
let result = value.ap(func);
52+
assert_eq!(result, None);
53+
}
54+
55+
#[test]
56+
fn test_result_apply_ok() {
57+
let value: Result<i32, String> = Ok(5);
58+
let func: Result<Box<dyn Fn(i32) -> i32>, String> =
59+
Ok(Box::new(|x: i32| x * 2) as Box<dyn Fn(i32) -> i32>);
60+
let result = value.ap(func);
61+
assert_eq!(result, Ok(10));
62+
}
63+
64+
#[test]
65+
fn test_result_apply_err_value() {
66+
let value: Result<i32, String> = Err("value error".to_string());
67+
let func: Result<Box<dyn Fn(i32) -> i32>, String> =
68+
Ok(Box::new(|x: i32| x * 2) as Box<dyn Fn(i32) -> i32>);
69+
let result = value.ap(func);
70+
assert_eq!(result, Err("value error".to_string()));
71+
}
72+
73+
#[test]
74+
fn test_result_apply_err_func() {
75+
let value: Result<i32, String> = Ok(5);
76+
let func: Result<Box<dyn Fn(i32) -> i32>, String> = Err("func error".to_string());
77+
let result = value.ap(func);
78+
assert_eq!(result, Err("func error".to_string()));
79+
}
80+
81+
// Applicative Laws
82+
#[test]
83+
fn test_applicative_identity_law_option() {
84+
// Identity: pure(id).ap(v) == v
85+
let v = Some(42);
86+
let id_func = Some(Box::new(|x: i32| x) as Box<dyn Fn(i32) -> i32>);
87+
let result = v.ap(id_func);
88+
assert_eq!(result, Some(42));
89+
}
90+
91+
#[test]
92+
fn test_applicative_homomorphism_option() {
93+
// Homomorphism: pure(f).ap(pure(x)) == pure(f(x))
94+
let x = 5;
95+
let f = |n: i32| n * 2;
96+
97+
let left = Option::of(x).ap(Some(Box::new(f) as Box<dyn Fn(i32) -> i32>));
98+
let right = Option::of(f(x));
99+
100+
assert_eq!(left, right);
101+
}
102+
103+
#[test]
104+
fn test_apply_with_type_change() {
105+
let value = Some(42);
106+
let func = Some(Box::new(|x: i32| x.to_string()) as Box<dyn Fn(i32) -> String>);
107+
let result = value.ap(func);
108+
assert_eq!(result, Some("42".to_string()));
109+
}
17110
}

fp-examples/src/foldable_example.rs

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[cfg(test)]
22
mod example {
3+
use fp_core::empty::Empty;
34
use fp_core::foldable::*;
45

56
/*
@@ -20,8 +21,126 @@ mod example {
2021

2122
#[test]
2223
fn fold_map_example() {
23-
let k = vec![Some(1 as i64), Some(2 as i64), Some(3 as i64), None];
24-
let result = fold_map(k, |&opt| if let Some(x) = opt { x } else { 0 });
24+
let k = vec![Some(1_i64), Some(2_i64), Some(3_i64), None];
25+
let result = fold_map(k, |&opt| opt.unwrap_or_default());
2526
assert_eq!(result, 6);
2627
}
28+
29+
// Additional comprehensive foldable tests
30+
#[test]
31+
fn test_vec_reduce() {
32+
let vec = vec![1, 2, 3, 4, 5];
33+
let result = vec.reduce(0, |acc, x| acc + x);
34+
assert_eq!(result, 15);
35+
}
36+
37+
#[test]
38+
fn test_vec_reduce_empty() {
39+
let vec: Vec<i32> = vec![];
40+
let result = vec.reduce(0, |acc, x| acc + x);
41+
assert_eq!(result, 0);
42+
}
43+
44+
#[test]
45+
fn test_vec_reduce_multiply() {
46+
let vec = vec![1, 2, 3, 4];
47+
let result = vec.reduce(1, |acc, x| acc * x);
48+
assert_eq!(result, 24);
49+
}
50+
51+
#[test]
52+
fn test_vec_reduce_string_concat() {
53+
let vec = vec!["hello", " ", "world"];
54+
let result = vec.reduce(String::new(), |acc, x| acc + x);
55+
assert_eq!(result, "hello world");
56+
}
57+
58+
#[test]
59+
fn test_vec_reduce_max() {
60+
let vec = vec![5, 2, 8, 1, 9, 3];
61+
let result = vec.reduce(i32::MIN, |acc, x| acc.max(*x));
62+
assert_eq!(result, 9);
63+
}
64+
65+
#[test]
66+
fn test_fold_map_sum() {
67+
let vec = vec![1, 2, 3, 4, 5];
68+
let result: i32 = fold_map(vec, |x| *x);
69+
assert_eq!(result, 15);
70+
}
71+
72+
#[test]
73+
fn test_fold_map_string_concat() {
74+
let vec = vec![1, 2, 3];
75+
let result: String = fold_map(vec, |x| x.to_string());
76+
assert_eq!(result, "123");
77+
}
78+
79+
#[test]
80+
fn test_fold_map_with_transformation() {
81+
let vec = vec![1, 2, 3];
82+
let result: i32 = fold_map(vec, |x| x * 2);
83+
assert_eq!(result, 12); // 2 + 4 + 6
84+
}
85+
86+
#[test]
87+
fn test_fold_map_empty_vec() {
88+
let vec: Vec<i32> = vec![];
89+
let result: i32 = fold_map(vec, |x| *x);
90+
assert_eq!(result, i32::empty());
91+
}
92+
93+
#[test]
94+
fn test_reduce_filter_operation() {
95+
let vec = vec![1, 2, 3, 4, 5, 6];
96+
let evens = vec.reduce(Vec::new(), |mut acc, x| {
97+
if x % 2 == 0 {
98+
acc.push(*x);
99+
}
100+
acc
101+
});
102+
assert_eq!(evens, vec![2, 4, 6]);
103+
}
104+
105+
#[test]
106+
fn test_reduce_partition() {
107+
#[derive(Debug, PartialEq)]
108+
struct Partitioned {
109+
evens: Vec<i32>,
110+
odds: Vec<i32>,
111+
}
112+
113+
let vec = vec![1, 2, 3, 4, 5];
114+
let result = vec.reduce(
115+
Partitioned {
116+
evens: vec![],
117+
odds: vec![],
118+
},
119+
|mut acc, x| {
120+
if x % 2 == 0 {
121+
acc.evens.push(*x);
122+
} else {
123+
acc.odds.push(*x);
124+
}
125+
acc
126+
},
127+
);
128+
129+
assert_eq!(result.evens, vec![2, 4]);
130+
assert_eq!(result.odds, vec![1, 3, 5]);
131+
}
132+
133+
#[test]
134+
fn test_reduce_count() {
135+
let vec = vec![1, 2, 3, 4, 5];
136+
let count = vec.reduce(0, |acc, _| acc + 1);
137+
assert_eq!(count, 5);
138+
}
139+
140+
#[test]
141+
fn test_vec_reduce_right() {
142+
let vec = vec![1, 2, 3, 4];
143+
let result = vec.reduce_right(0, |x, acc| x + acc);
144+
assert_eq!(result, 10);
145+
}
27146
}

0 commit comments

Comments
 (0)