aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2023-04-02 21:38:17 +0200
committerAndreas Grois <andi@grois.info>2023-04-02 21:53:09 +0200
commit418f514fc46f45ae2901753e3398adb33664bed9 (patch)
treef8edc0df880a00d4964877d4b835c0eb9e481210
parent02d01dd1b544a576caeb8da42912c5db904b94bd (diff)
I, for one, welcome our new clippy overlords
-rw-r--r--clippy.toml1
-rw-r--r--examples/text-adventure/data.rs25
-rw-r--r--examples/text-adventure/dsl.rs2
-rw-r--r--examples/text-adventure/logic.rs22
-rw-r--r--examples/text-adventure/main.rs16
-rw-r--r--examples/text-adventure/side_effects.rs10
-rw-r--r--src/lib.rs242
-rw-r--r--tests/multiple_generics.rs49
-rw-r--r--tests/multiple_generics_lifetime.rs35
-rw-r--r--tests/trivial.rs8
-rw-r--r--tests/trivial_lifetime.rs8
-rw-r--r--tests/vector.rs149
-rw-r--r--tests/with_lifetimes.rs2
13 files changed, 261 insertions, 308 deletions
diff --git a/clippy.toml b/clippy.toml
new file mode 100644
index 0000000..12b8e80
--- /dev/null
+++ b/clippy.toml
@@ -0,0 +1 @@
+doc-valid-idents = [ "eDSL", "eDSLs", ".." ] \ No newline at end of file
diff --git a/examples/text-adventure/data.rs b/examples/text-adventure/data.rs
index 2c12bb0..3e9c49e 100644
--- a/examples/text-adventure/data.rs
+++ b/examples/text-adventure/data.rs
@@ -83,19 +83,13 @@ pub enum Item{
impl Item {
fn price(self) -> usize {
match self {
- Item::SausageRoll => 300,
- Item::Pickles => 250,
- Item::Milk => 125,
- Item::Yoghurt => 125,
+ Item::SausageRoll | Item::FishSandwich | Item::Shots => 300,
+ Item::Pickles | Item::Pulp => 250,
+ Item::Milk | Item::Yoghurt | Item::Beer => 125,
Item::Cheese => 750,
Item::CatFood => 2500,
- Item::Beer => 125,
Item::ToiletPaper => 500,
- Item::FishSandwich => 300,
Item::ChewingGum => 100,
- Item::Shots => 300,
- Item::Pulp => 250,
-
}
}
pub fn description(self) -> &'static str {
@@ -112,7 +106,6 @@ impl Item {
Item::ChewingGum => "A pack of chewing gum, costing €1.00",
Item::Shots => "A shot of a sad excuse for whisky, costing €3.00",
Item::Pulp => "A pulp novel called \"Aliens ate my trashbin\", which should not cost the €2.50 it does",
-
}
}
}
@@ -131,17 +124,11 @@ impl Location{
//In a real project I would probably aim to make this Copy as well, especially if it's as small as this.
//I left it as Clone intentionally, to illustrate how one can work around the limitation of it not being Copy.
-#[derive(Clone)]
+#[derive(Clone, Default)]
pub struct Inventory {
pub items : Vec<Item>,
}
-impl Default for Inventory{
- fn default() -> Self {
- Self { items: Default::default() }
- }
-}
-
impl Inventory{
pub fn has_item_from_room(&self, room : Location) -> bool {
let items_from_room = room.items();
@@ -151,7 +138,7 @@ impl Inventory{
if self.items.len() < 3 {
let mut items = self.items;
items.push(item); //am I the only one that hates that push doesn't return the updated vec?
- Ok(Inventory{items, ..self})
+ Ok(Inventory{items})
} else {
Err(self)
}
@@ -170,7 +157,7 @@ impl Inventory{
"€10.00" //It doesn't change. Can as well be a constant.
}
pub fn total_price(&self) -> usize {
- self.items.iter().cloned().map(Item::price).sum::<usize>()
+ self.items.iter().copied().map(Item::price).sum::<usize>()
}
pub fn can_afford(&self) -> bool {
self.total_price() <= 1000_usize
diff --git a/examples/text-adventure/dsl.rs b/examples/text-adventure/dsl.rs
index 44bc978..c448f04 100644
--- a/examples/text-adventure/dsl.rs
+++ b/examples/text-adventure/dsl.rs
@@ -5,7 +5,7 @@ use std::{rc::Rc, borrow::Cow};
use higher_free_macro::free;
use std::convert::identity;
use higher::Functor;
-use super::data::*;
+use super::data::{Location, Mood, Speaker};
#[derive(Clone)]
pub enum SausageRoll<'a, 's,A>{
diff --git a/examples/text-adventure/logic.rs b/examples/text-adventure/logic.rs
index dc3ff45..d884598 100644
--- a/examples/text-adventure/logic.rs
+++ b/examples/text-adventure/logic.rs
@@ -4,8 +4,8 @@
use std::borrow::Cow;
use higher::{run, Functor, Pure, Bind};
-use super::data::*;
-use super::dsl::*;
+use super::data::{Inventory, Item, Location, Mood, Speaker};
+use super::dsl::{FreeSausageRoll, exposition, give_player_options, present_location, say_dialogue_line};
//Haskell has a when function, and it's nice. Sooo, copy that.
macro_rules! when {
@@ -24,7 +24,7 @@ pub fn game<'a,'s : 'a>() -> FreeSausageRoll<'a, 's, ()>{
c <= intro();
when!{c => {
//handle_rooms is the main game loop: Go from room to room.
- c <= handle_rooms(Location::Refrigerators, Default::default()); //can't destructure in assignment in higher-0.2. Maybe later.
+ c <= handle_rooms(Location::Refrigerators, Inventory::default()); //can't destructure in assignment in higher-0.2. Maybe later.
//if we ended up here, we left the supermarket.
ending(c.1)
}}
@@ -66,7 +66,7 @@ fn ending<'a,'s:'a>(inventory : Inventory) -> FreeSausageRoll<'a, 's, ()>{
} else {
run!{
say_dialogue_line(Speaker::Partner, Cow::from("What did you do in there? I asked you to bring me a sausage roll..."), Mood::Annoyed);
- when!{inventory.items.len() > 0 => {
+ when!{!inventory.items.is_empty() => {
say_dialogue_line(Speaker::Partner, Cow::from("Also, why did you buy all that other stuff?"), Mood::Annoyed)
}};
say_dialogue_line(Speaker::Partner, Cow::from("Well, let's move on, but don't complain if I get hangry on the way."), Mood::Annoyed)
@@ -77,13 +77,11 @@ fn ending<'a,'s:'a>(inventory : Inventory) -> FreeSausageRoll<'a, 's, ()>{
fn handle_rooms<'a,'s:'a>(room: Location, inventory : Inventory) -> FreeSausageRoll<'a, 's, (Location, Inventory)>{
run!{
c <= handle_room(room, inventory);
- if c.0 != Location::Entrance {
+ if c.0 == Location::Entrance {
+ FreeSausageRoll::pure(c)
+ } else {
//If this were an actual game, we could put a save-point here. At this location the next room to handle is just determined by room and inventory.
handle_rooms(c.0, c.1)
- } else {
- run!{
- yield c
- }
}
}
}
@@ -185,7 +183,7 @@ fn handle_deli<'a,'s:'a>(inventory : Inventory) -> FreeSausageRoll<'a, 's, (Loca
3 => {
run!{
i <= talk_to_deli_lady(inventory.clone());
- handle_deli(i.clone())
+ handle_deli(i)
}
},
_ => unreachable!()
@@ -215,7 +213,7 @@ fn handle_checkout<'a,'s:'a>(inventory : Inventory) -> FreeSausageRoll<'a, 's, (
FreeSausageRoll::pure((Location::Entrance, inventory.clone()))
}
},
- Err(inventory) => handle_checkout(inventory.clone()),
+ Err(inventory) => handle_checkout(inventory),
}
}
},
@@ -275,7 +273,7 @@ fn return_item<'a,'s:'a>(inventory : Inventory, room : Location) -> FreeSausageR
//similar to try_take_item here the inventory can't easily be captured. For illustration purposes, here we
//don't pass it along as clones, but rather return from do-notation to capture it.
let items_in_room = room.items();
- let carried_items_from_here = inventory.items.iter().filter(|i| items_in_room.contains(i)).cloned().collect::<Vec<_>>();
+ let carried_items_from_here = inventory.items.iter().filter(|i| items_in_room.contains(i)).copied().collect::<Vec<_>>();
//ownership shmownership
let carried_items_from_here2 = carried_items_from_here.clone();
let chosen = run! {
diff --git a/examples/text-adventure/main.rs b/examples/text-adventure/main.rs
index c98441b..1d538cc 100644
--- a/examples/text-adventure/main.rs
+++ b/examples/text-adventure/main.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! A small example text adventure, the logic of which is implemented as a Free Monad based eDSL.
//!
//! The goal of this game is to buy a sausage roll. With pickle.
@@ -11,17 +13,17 @@
//!
//! In a real project, I'd just make all game state (here: inventory) `Copy`, and use owned values wherever possible to make the code
//! more concise. If `Copy` is not an option, I'd probably make a custom version of `run!{}` that allows to clone the
-//! game state in a convenient way (see https://github.com/bodil/higher/issues/6).
+//! game state in a convenient way (see [higher issue 6](https://github.com/bodil/higher/issues/6)).
//!
//! But on to the explanation what is going on:
//! This project has 4 modules:
-//! - "data" contains the data. Stuff like item types, item descriptions, rooms, etc.
-//! - "dsl" contains the embedded domain specific language. In other words, a Functor and the corresponding Free Monad type (and some helpers)
-//! - "logic" describes the game's main logic using the language defined in "dsl"
-//! - "side_effects" actually runs the logic.
+//! - `data` contains the data. Stuff like item types, item descriptions, rooms, etc.
+//! - `dsl` contains the embedded domain specific language. In other words, a Functor and the corresponding Free Monad type (and some helpers)
+//! - `logic` describes the game's main logic using the language defined in "dsl"
+//! - `side_effects` actually runs the logic.
//!
-//! The important part here is that all the stuff that isn't in "side_effects" is independent of the concrete implementation of "side_effects".
-//! The current "side_effects" runs a text-adventure, but it could just as well render as a visual-novel, without the need to touch any of the other modules.
+//! The important part here is that all the stuff that isn't in `side_effects` is independent of the concrete implementation of `side_effects`.
+//! The current `side_effects` runs a text-adventure, but it could just as well render as a visual-novel, without the need to touch any of the other modules.
mod data;
mod dsl;
diff --git a/examples/text-adventure/side_effects.rs b/examples/text-adventure/side_effects.rs
index ed0f9e1..dd911dd 100644
--- a/examples/text-adventure/side_effects.rs
+++ b/examples/text-adventure/side_effects.rs
@@ -11,23 +11,23 @@ pub fn run<'a, 's:'a>(mut game : FreeSausageRoll<'a, 's, ()>) -> std::io::Result
while let FreeSausageRoll::Free(command) = game {
game = match *command {
crate::dsl::SausageRoll::SayDialogueLine { speaker, text, mood, next } => {
- println!("{} says: \"{}\" with {} on their face.", speaker.text_description(), text, mood.text_description());
+ println!("{} says: \"{text}\" with {} on their face.", speaker.text_description(), mood.text_description());
next
},
crate::dsl::SausageRoll::GivePlayerOptions { options, next } =>
{
println!("Your options are:");
for (id, option) in options.iter().enumerate().map(|(i,o)| (i+1,o)){
- println!("{}: {}", id, option);
+ println!("{id}: {option}");
}
let mut input = String::new();
let mut chosen;
- while let None = {
+ while {
input.clear();
std::io::stdin().read_line(&mut input)?;
chosen = input.trim().parse().ok().filter(|o : &usize| *o > 0 && *o <= options.len());
- chosen
+ chosen.is_none()
} {
println!("Invalid choice. Please select one of the options given above.");
}
@@ -39,7 +39,7 @@ pub fn run<'a, 's:'a>(mut game : FreeSausageRoll<'a, 's, ()>) -> std::io::Result
next
},
crate::dsl::SausageRoll::Exposition { text, next } => {
- println!("{}", text);
+ println!("{text}");
next
},
};
diff --git a/src/lib.rs b/src/lib.rs
index 9824a21..da8d67c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! A macro that uses the traits from the [higher] crate and generates a Free [`Monad`][higher::Monad] type for a given [`Functor`][higher::Functor].
//!
//! This is a port of the Control.Monad.Free part of the ["free" Haskell package](https://hackage.haskell.org/package/free) by Edward Kmett.
@@ -359,17 +361,17 @@ mod free_monad_tests{
#[test]
fn test_lift_f_no_lifetime(){
- let f = FreeVec::lift_f(vec![1,2,3]);
- match f {
- FreeVec::Free(v) => {
- match &**v {
+ let free = FreeVec::lift_f(vec![1,2,3]);
+ match free {
+ FreeVec::Free(vector) => {
+ match &**vector {
[FreeVec::Pure(a),FreeVec::Pure(b),FreeVec::Pure(c)] => {
assert_eq!(vec![*a,*b,*c], vec![1,2,3]);
},
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeVec::Pure(_) => unreachable!()
}
}
@@ -392,33 +394,41 @@ mod free_monad_tests{
#[test]
fn test_fmap_no_lifetime(){
let f = FreeVec::lift_f(vec![1,2,3]);
- let f = f.fmap(|x| (x as f32)/2.0);
+ let f = f.fmap(|x| (f64::from(x))/2.0);
match f {
FreeVec::Free(f) => {
match &**f{
[FreeVec::Pure(a), FreeVec::Pure(b), FreeVec::Pure(c)] => {
- assert_eq!(vec![0.5f32, 1f32, 1.5f32], vec![*a,*b,*c]);
+ assert_eq!(vec![0.5f64, 1f64, 1.5f64], vec![*a,*b,*c]);
},
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeVec::Pure(_) => unreachable!()
}
}
+ //just to appease clippy without disabling the lint....
+ macro_rules! assert_nearly_equal {
+ ($a:expr, $b:expr, $c:expr) => {
+ assert!((($a)-($b)).abs() < $c)
+ };
+ }
+
#[test]
fn test_bind_no_lifetime(){
let f = FreeVec::lift_f(vec![1,2]);
- let f = f.bind(|x| if x % 2 == 0 { FreeVec::lift_f(vec![x as f32,x as f32 + 1.0f32])} else { FreeVec::Pure(x as f32)});
+ let f = f.bind(|x| if x % 2 == 0 { FreeVec::lift_f(vec![f64::from(x),f64::from(x) + 1.0f64])} else { FreeVec::Pure(f64::from(x))});
match f {
FreeVec::Free(f) => {
match &**f {
[FreeVec::Pure(a),FreeVec::Free(b)] => {
- assert_eq!(*a, 1.0f32);
+
+ assert_nearly_equal!(*a, 1.0f64, f64::EPSILON);
match &***b {
[FreeVec::Pure(a), FreeVec::Pure(b)] => {
- assert_eq!(*a, 2.0f32);
- assert_eq!(*b, 3.0f32);
+ assert_nearly_equal!(*a, 2.0f64, f64::EPSILON);
+ assert_nearly_equal!(*b, 3.0f64, f64::EPSILON);
},
_ => unreachable!()
}
@@ -426,27 +436,27 @@ mod free_monad_tests{
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeVec::Pure(_) => unreachable!()
}
}
#[test]
fn test_apply_no_lifetime(){
- let f = FreeVec::Free(Box::new(vec![FreeVec::Free(Box::new(vec![FreeVec::Pure((|x| (x as i32)*2) as fn(u32) -> i32), FreeVec::Pure((|x| (x as i32)+2) as fn(u32)->i32)])), FreeVec::Pure((|x| (x as i32)-5) as fn(u32)->i32)]));
- let m = FreeVec::Free(Box::new(vec![FreeVec::Pure(5u32), FreeVec::Free(Box::new(vec![FreeVec::Pure(6u32), FreeVec::Pure(7u32)]))]));
- let m = m.apply(f.fmap(Into::into));
+ let functions = FreeVec::Free(Box::new(vec![FreeVec::Free(Box::new(vec![FreeVec::Pure((|x| i64::from(x)*2) as fn(u32) -> i64), FreeVec::Pure((|x| i64::from(x)+2) as fn(u32)->i64)])), FreeVec::Pure((|x| i64::from(x)-5) as fn(u32)->i64)]));
+ let free_monad = FreeVec::Free(Box::new(vec![FreeVec::Pure(5u32), FreeVec::Free(Box::new(vec![FreeVec::Pure(6u32), FreeVec::Pure(7u32)]))]));
+ let free_monad = free_monad.apply(functions.fmap(Into::into));
//what have I gotten myself into...
//at least the mapped sub-trees are all identical in shape to m, so, they can be tested by the same test function...
- let check_mlike_structure = |m : &FreeVec<_>, p1,p2,p3| {
- match m {
- FreeVec::Free(m) => {
- match &***m{
- [FreeVec::Pure(l),FreeVec::Free(r)] => {
- assert_eq!(*l,p1);
- match &***r{
- [FreeVec::Pure(l), FreeVec::Pure(r)] => {
- assert_eq!(*l, p2);
- assert_eq!(*r, p3);
+ let check_mlike_structure = |free_monad : &FreeVec<_>, pure1,pure2,pure3| {
+ match free_monad {
+ FreeVec::Free(vector) => {
+ match &***vector{
+ [FreeVec::Pure(left),FreeVec::Free(right)] => {
+ assert_eq!(*left,pure1);
+ match &***right{
+ [FreeVec::Pure(left), FreeVec::Pure(right)] => {
+ assert_eq!(*left, pure2);
+ assert_eq!(*right, pure3);
},
_ => unreachable!()
}
@@ -454,27 +464,27 @@ mod free_monad_tests{
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeVec::Pure(_) => unreachable!()
}
};
//now, where are those sub-trees exactly, in this monstrosity?
- match m {
- FreeVec::Free(m) => {
- match &**m{
- [FreeVec::Free(l), r] => {
- match &***l {
+ match free_monad {
+ FreeVec::Free(vector) => {
+ match &**vector{
+ [FreeVec::Free(left), right] => {
+ match &***left {
[a,b] => {
- check_mlike_structure(a, 10,12,14);
- check_mlike_structure(b, 7,8,9);
+ check_mlike_structure(a, 10i64,12i64,14i64);
+ check_mlike_structure(b, 7i64,8i64,9i64);
},
_ => unreachable!()
}
- check_mlike_structure(r, 0,1,2)
+ check_mlike_structure(right, 0i64,1i64,2i64);
},
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeVec::Pure(_) => unreachable!()
}
}
@@ -495,7 +505,7 @@ mod free_monad_tests{
}
//need Bind and Pure to test retract. This is dumb, but it should fulfill the monad laws:
- impl<'a, A : 'a, B : 'a> Bind<'a, A> for Conti<'a,A,B> where B : Clone{
+ impl<'a, A : 'a, B : Clone+'a> Bind<'a, A> for Conti<'a,A,B>{
type Target<T> = Conti<'a,T,B>;
fn bind<C, F>(self, f: F) -> Self::Target<C> where F: Fn(A) -> Self::Target<C> + 'a {
@@ -506,7 +516,7 @@ mod free_monad_tests{
Conti(Rc::new(move |x| (l(x.clone())).0(x)), Rc::new(move |x| (r(x.clone())).1(x)))
}
}
- impl<'a, A : 'a, B : 'a> Pure<A> for Conti<'a,A,B> where A : Clone{
+ impl<'a, A : Clone+'a, B : 'a> Pure<A> for Conti<'a,A,B>{
fn pure(value: A) -> Self {
let v2 = value.clone();
Conti(Rc::new(move |_| value.clone()), Rc::new(move |_| v2.clone()))
@@ -516,33 +526,31 @@ mod free_monad_tests{
//I really am not certain if the Pure and Bind above are correct. Sooo, why not test those too, while we are at it?
#[test]
fn test_conti_monad_laws(){
- let b = |x : u32| {
- let y = x.clone();
- Conti::<u32,u32>(Rc::new(move |a| x.clone() + a*2), Rc::new(move |a| y.clone() * a+3))
+ let binder = |x : u32| {
+ let y = x;
+ Conti::<u32,u32>(Rc::new(move |a| x + a*2), Rc::new(move |a| y * a+3))
};
- let t1 = Conti::pure(7u32);
- let v1 = t1.bind(b);
- let v2 = b(7u32);
+ let test1 = Conti::pure(7u32);
+ let v1 = test1.bind(binder);
+ let v2 = binder(7u32);
assert_eq!((v1.0)(13), (v2.0)(13));
assert_eq!((v1.1)(17), (v2.1)(17));
- let c = Conti(Rc::new(|a| 31 + a*5), Rc::new(|b| 32*b+3));
- let d =c.clone().bind(Conti::pure);
- assert_eq!((c.0)(3), (d.0)(3));
- assert_eq!((c.1)(5), (d.1)(5));
+ let test2 = Conti(Rc::new(|a| 31 + a*5), Rc::new(|b| 32*b+3));
+ let bound_with_pure =test2.clone().bind(Conti::pure);
+ assert_eq!((test2.0)(3), (bound_with_pure.0)(3));
+ assert_eq!((test2.1)(5), (bound_with_pure.1)(5));
- let m = Conti(Rc::new(|a| 32 + (a*2)), Rc::new(|b| 32*b+7));
+ let test3 = Conti(Rc::new(|a| 32 + (a*2)), Rc::new(|b| 32*b+7));
let g = |x : u32| {
- let y = x.clone();
- Conti::<u32,u32>(Rc::new(move |a| x.clone()*2 + a), Rc::new(move |a| y.clone() * a+7))
+ Conti::<u32,u32>(Rc::new(move |a| x*2 + a), Rc::new(move |a| x * a+7))
};
let h = |x : u32| {
- let y = x.clone();
- Conti::<u32,u32>(Rc::new(move |a| x.clone() + a), Rc::new(move |a| y.clone() * a+12))
+ Conti::<u32,u32>(Rc::new(move |a| x + a), Rc::new(move |a| x * a+12))
};
- let v1 = (m.clone().bind(g.clone())).bind(h.clone());
- let v2 = m.bind(|a| (g(a).bind(h)));
+ let v1 = (test3.clone().bind(g)).bind(h);
+ let v2 = test3.bind(|a| (g(a).bind(h)));
assert_eq!((v1.0)(37), (v2.0)(37));
assert_eq!((v1.1)(41), (v2.1)(41));
@@ -559,14 +567,14 @@ mod free_monad_tests{
FreeConti::Free(m) => {
match (m.0)(4){
FreeConti::Pure(v) => assert_eq!(v, 8),
- _ => unreachable!()
+ FreeConti::Free(_) => unreachable!()
}
match (m.1)(4){
FreeConti::Pure(v) => assert_eq!(v, 9),
- _ => unreachable!()
+ FreeConti::Free(_) => unreachable!()
}
},
- _ => unreachable!()
+ FreeConti::Pure(_) => unreachable!()
}
}
@@ -580,27 +588,27 @@ mod free_monad_tests{
#[test]
fn test_fmap_lifetime(){
- let c = Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5));
- let f = FreeConti::lift_f(c);
- let f = f.fmap(|x : i32| (x as f32)*0.25f32);
- match f {
+ let functor = Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5));
+ let free_monad = FreeConti::lift_f(functor);
+ let free_monad = free_monad.fmap(|x : i32| f64::from(x)*0.25f64);
+ match free_monad {
FreeConti::Free(f) => {
- let l = (f.0)(7);
- match l {
+ let left = (f.0)(7);
+ match left {
FreeConti::Pure(v) => {
- assert_eq!(v, 5.75f32);
+ assert_nearly_equal!(v, 5.75f64, f64::EPSILON);
},
- _ => unreachable!()
+ FreeConti::Free(_) => unreachable!()
}
- let r = (f.1)(7);
- match r {
+ let right = (f.1)(7);
+ match right {
FreeConti::Pure(v) => {
- assert_eq!(v, 11.25f32);
+ assert_nearly_equal!(v, 11.25f64, f64::EPSILON);
},
- _ => unreachable!()
+ FreeConti::Free(_) => unreachable!()
}
},
- _ => unreachable!()
+ FreeConti::Pure(_) => unreachable!()
}
}
@@ -615,101 +623,101 @@ mod free_monad_tests{
#[test]
fn test_bind_lifetime(){
- let c = Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5));
- let f = FreeConti::lift_f(c);
- let f = f.bind(|y| {
- let z = y.clone();
- FreeConti::lift_f(Conti(Rc::new(move |x| (x as f32)*0.25f32 + (y as f32)), Rc::new(move |x| (x as f32) * 0.5f32 - (z as f32))))
+ let functor = Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5));
+ let free_monad = FreeConti::lift_f(functor);
+ let free_monad = free_monad.bind(|y| {
+ let z = y;
+ FreeConti::lift_f(Conti(Rc::new(move |x| f64::from(x)*0.25f64 + f64::from(y)), Rc::new(move |x| f64::from(x) * 0.5f64 - f64::from(z))))
});
- match f {
+ match free_monad {
FreeConti::Free(f) => {
- let l = (f.0)(4);
- match l {
+ let left = (f.0)(4);
+ match left {
FreeConti::Free(f) => {
//14i32
- let l = (f.0)(5);
- match l {
- FreeConti::Pure(v) => assert_eq!(v, 15.25f32),
+ let left = (f.0)(5);
+ match left {
+ FreeConti::Pure(v) => assert_nearly_equal!(v, 15.25f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
- let r = (f.1)(5);
- match r{
- FreeConti::Pure(v) => assert_eq!(v, -11.5f32),
+ let right = (f.1)(5);
+ match right{
+ FreeConti::Pure(v) => assert_nearly_equal!(v, -11.5f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
},
FreeConti::Pure(_) => unreachable!(),
}
- let r = (f.1)(4);
- match r {
+ let right = (f.1)(4);
+ match right {
FreeConti::Free(f) => {
//30i32
- let l = (f.0)(5);
- match l {
- FreeConti::Pure(v) => assert_eq!(v, 31.25f32),
+ let left = (f.0)(5);
+ match left {
+ FreeConti::Pure(v) => assert_nearly_equal!(v, 31.25f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
- let r = (f.1)(5);
- match r {
- FreeConti::Pure(v) => assert_eq!(v, -27.5f32),
+ let right = (f.1)(5);
+ match right {
+ FreeConti::Pure(v) => assert_nearly_equal!(v, -27.5f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
},
FreeConti::Pure(_) => unreachable!(),
}
},
- _ => unreachable!()
+ FreeConti::Pure(_) => unreachable!()
}
}
#[test]
fn test_apply_lifetime(){
//oh, god, please no.
- let m = FreeConti::lift_f(Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5)));
- let f = FreeConti::lift_f(Conti(Rc::new(|x : u32| -> ApplyFn<i32,f32> {
- (move |y : i32| (y + (x as i32)) as f32).into()
+ let free_monad_input = FreeConti::lift_f(Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5)));
+ let functions = FreeConti::lift_f(Conti(Rc::new(|x : u32| -> ApplyFn<i32,f64> {
+ (move |y : i32| f64::from(y + i32::try_from(x).unwrap())).into()
}), Rc::new(|x : u32| {
- (move |y : i32| (y*(x as i32)) as f32).into()
+ (move |y : i32| f64::from(y*i32::try_from(x).unwrap())).into()
})));
//make it stop!
- let m = m.apply(f);
+ let free_monad = free_monad_input.apply(functions);
- match m {
+ match free_monad {
FreeConti::Free(m) => {
- let l = (m.0)(5u32);
- match l {
+ let left = (m.0)(5u32);
+ match left {
FreeConti::Free(m) => {
- let l = (m.0)(7u32);
- match l {
- FreeConti::Pure(v) => assert_eq!(v,28f32),
+ let left = (m.0)(7u32);
+ match left {
+ FreeConti::Pure(v) => assert_nearly_equal!(v,28f64,f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
- let r = (m.1)(7u32);
- match r {
- FreeConti::Pure(v) => assert_eq!(v,50f32),
+ let right = (m.1)(7u32);
+ match right {
+ FreeConti::Pure(v) => assert_nearly_equal!(v,50f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
},
FreeConti::Pure(_) => unreachable!(),
}
- let r = (m.1)(5u32);
- match r {
+ let right = (m.1)(5u32);
+ match right {
FreeConti::Free(m) => {
- let l = (m.0)(7u32);
- match l {
- FreeConti::Pure(v) => assert_eq!(v,(5f32*23f32)),
+ let left = (m.0)(7u32);
+ match left {
+ FreeConti::Pure(v) => assert_nearly_equal!(v,5f64*23f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
- let r = (m.1)(7u32);
- match r {
- FreeConti::Pure(v) => assert_eq!(v, 5f32*45f32),
+ let right = (m.1)(7u32);
+ match right {
+ FreeConti::Pure(v) => assert_nearly_equal!(v, 5f64*45f64, f64::EPSILON),
FreeConti::Free(_) => unreachable!(),
}
},
FreeConti::Pure(_) => unreachable!(),
}
},
- _ => unreachable!()
+ FreeConti::Pure(_) => unreachable!()
}
//let's never speak of this again.
}
diff --git a/tests/multiple_generics.rs b/tests/multiple_generics.rs
index 2f560dd..c8c76ba 100644
--- a/tests/multiple_generics.rs
+++ b/tests/multiple_generics.rs
@@ -1,11 +1,21 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! Tests if multiple generic parameters work, for the case that lifetimes are independent of mapping functions.
-//! For simplicity, it just creates a FreeResult
+//! For simplicity, it just creates a `FreeResult` based on `Result`.
use higher_free_macro::free;
use higher::{Functor, Bind, Apply};
free!(FreeResult<O,E>, Result<FreeResult<O,E>,E>);
+
+//just to appease clippy without disabling the lint....
+macro_rules! assert_nearly_equal {
+ ($a:expr, $b:expr, $c:expr) => {
+ assert!((($a)-($b)).abs() < $c)
+ };
+}
+
#[test]
fn test_multiple_generics(){
let m : FreeResult<_, String> = FreeResult::lift_f(Ok(37u32));
@@ -16,26 +26,16 @@ fn test_multiple_generics(){
match m {
FreeResult::Free(b) => {
match *b {
- Ok(f) => {
- match f {
- FreeResult::Free(b) => {
- match *b {
- Ok(f) => {
- match f{
- FreeResult::Pure(x) => assert_eq!(x, 37*6),
- _ => unreachable!()
- }
- }
- _ => unreachable!()
- }
- },
+ Ok(FreeResult::Free(b)) => {
+ match *b {
+ Ok(FreeResult::Pure(x)) => assert_eq!(x, 37*6),
_ => unreachable!()
}
}
_ => unreachable!()
}
}
- _ => unreachable!()
+ FreeResult::Pure(_) => unreachable!()
}
}
@@ -46,41 +46,36 @@ fn test_multiple_generics2(){
match m{
FreeResult::Free(m) => {
match &*m {
- Ok(m) => {
- match m{
- FreeResult::Free(m) => {
- match &**m {
- Err(e) => assert_eq!(e, "An early out."),
- _ => unreachable!()
- }
- }
+ Ok(FreeResult::Free(m)) => {
+ match &**m {
+ Err(e) => assert_eq!(e, "An early out."),
_ => unreachable!()
}
},
_ => unreachable!()
}
},
- _ => unreachable!()
+ FreeResult::Pure(_) => unreachable!()
}
}
#[test]
fn test_multiple_generics3(){
let m : FreeResult<_, String> = FreeResult::lift_f(Ok(37u32));
- let f : FreeResult<_, String> = FreeResult::Pure(|x : u32| -> f32 {(x as f32)*0.5f32}).fmap(Into::into);
+ let f : FreeResult<_, String> = FreeResult::Pure(|x : u32| -> f64 {f64::from(x)*0.5f64}).fmap(Into::into);
let m = m.apply(f);
match m{
FreeResult::Free(m) => {
match &*m{
Ok(k) => {
match k {
- FreeResult::Pure(k) => assert_eq!(18.5f32, *k),
+ FreeResult::Pure(k) => assert_nearly_equal!(18.5f64, *k, f64::EPSILON),
FreeResult::Free(_) => unreachable!(),
}
}
Err(_) => unreachable!(),
}
},
- _ => unreachable!()
+ FreeResult::Pure(_) => unreachable!()
}
} \ No newline at end of file
diff --git a/tests/multiple_generics_lifetime.rs b/tests/multiple_generics_lifetime.rs
index ddc0628..48a9bd1 100644
--- a/tests/multiple_generics_lifetime.rs
+++ b/tests/multiple_generics_lifetime.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! Tests if multiple generic parameters work, if the return value's lifetime depends on the mapping function lifetime.
use std::rc::Rc;
@@ -24,10 +26,10 @@ free!(<'xx>, FreeTest<'xx,'yy,AA,BB>, TestFunctor<'xx, 'yy, FreeTest<'xx, 'yy, A
#[test]
fn test_lifetime_multiple_generics(){
- let m = FreeTest::lift_f(TestFunctor{ data : &"Listening to NSP while writing this.", next : Rc::new(|x| (x as f32)*0.5f32)});
- let f = FreeTest::Pure(|x : f32| -> bool {x > 0.7f32} ).fmap(Into::into);
- let m = m.apply(f);
- match m {
+ let free_monad = FreeTest::lift_f(TestFunctor{ data : &"Listening to NSP while writing this.", next : Rc::new(|x| f64::from(x)*0.5f64)});
+ let functions = FreeTest::Pure(|x : f64| -> bool {x > 0.7f64} ).fmap(Into::into);
+ let free_monad_after_apply = free_monad.apply(functions);
+ match free_monad_after_apply {
FreeTest::Free(m) => {
assert_eq!(m.data, &"Listening to NSP while writing this.");
let x = m.next.clone();
@@ -43,24 +45,31 @@ fn test_lifetime_multiple_generics(){
FreeTest::Free(_) => unreachable!(),
}
},
- _ => unreachable!()
+ FreeTest::Pure(_) => unreachable!()
}
}
+//just to appease clippy without disabling the lint....
+macro_rules! assert_nearly_equal {
+ ($a:expr, $b:expr, $c:expr) => {
+ assert!((($a)-($b)).abs() < $c)
+ };
+}
+
#[test]
fn test_lifetime_multiple_generics_bind(){
- let m = FreeTest::lift_f(TestFunctor{ data : &"Listening to Soilwork while writing this.", next : Rc::new(|x| (x as f32)*0.5f32)});
- let m = m.bind(|x : f32| -> FreeTest<_,_> {
+ let m = FreeTest::lift_f(TestFunctor{ data : &"Listening to Soilwork while writing this.", next : Rc::new(|x| f64::from(x)*0.5f64)});
+ let m = m.bind(|x : f64| -> FreeTest<_,_> {
if x < 0.0 {
- FreeTest::Pure(x.abs().floor() as u32)
+ FreeTest::Pure(x.abs().floor())
} else {
- FreeTest::lift_f(TestFunctor{data : &"Now it's Little Big.", next : Rc::new(move |y| (y as u32) + (x.ceil() as u32))})
+ FreeTest::lift_f(TestFunctor{data : &"Now it's Little Big.", next : Rc::new(move |y| f64::from(y) + x.ceil())})
}});
match m{
FreeTest::Free(m) => {
assert_eq!(m.data, &"Listening to Soilwork while writing this.");
match (m.next)(-3){
- FreeTest::Pure(v) => assert_eq!(v, 1),
+ FreeTest::Pure(v) => assert_nearly_equal!(v, 1f64, f64::EPSILON),
FreeTest::Free(_) => unreachable!(),
}
match (m.next)(3){
@@ -68,14 +77,12 @@ fn test_lifetime_multiple_generics_bind(){
FreeTest::Free(v) => {
assert_eq!(v.data, &"Now it's Little Big.");
match (v.next)(5) {
- FreeTest::Pure(v) => {
- assert_eq!(v, 7)
- },
+ FreeTest::Pure(v) => assert_nearly_equal!(v, 7f64, f64::EPSILON),
FreeTest::Free(_) => unreachable!(),
}
},
}
},
- _ => unreachable!()
+ FreeTest::Pure(_) => unreachable!()
}
} \ No newline at end of file
diff --git a/tests/trivial.rs b/tests/trivial.rs
index 5468fe4..5bc1e91 100644
--- a/tests/trivial.rs
+++ b/tests/trivial.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! A trivial test functor. Not holding any data, so this is basically just a linked list of free-nodes.
use higher_free_macro::free;
use higher::{Functor, Bind, Apply};
@@ -24,16 +26,16 @@ fn test_trivial_functor() {
TrivialFunctor(f) => {
match f{
TrivialFreeMonad::Pure(x) => assert_eq!(x, 37*6),
- _ => unreachable!()
+ TrivialFreeMonad::Free(_) => unreachable!()
}
}
}
},
- _ => unreachable!()
+ TrivialFreeMonad::Pure(_) => unreachable!()
}
}
}
}
- _ => unreachable!()
+ TrivialFreeMonad::Pure(_) => unreachable!()
}
} \ No newline at end of file
diff --git a/tests/trivial_lifetime.rs b/tests/trivial_lifetime.rs
index fe05234..4f7d094 100644
--- a/tests/trivial_lifetime.rs
+++ b/tests/trivial_lifetime.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! Tests if a trivial functor, which's lifetime depends on the mapping function, works.
use std::rc::Rc;
use higher::{Functor, Bind};
@@ -22,8 +24,8 @@ free!(<'a>, FreeTriv<'a,A,B>, TrivWithLifetime<'a,FreeTriv<'a,A,B>,B>);
#[test]
fn test_trivial_with_lifetime(){
- let f = FreeTriv::lift_f(TrivWithLifetime{next : Rc::new(|x : i32| x.abs() as u32)});
- let f = f.bind(|x| FreeTriv::Pure(x));
+ let f = FreeTriv::lift_f(TrivWithLifetime{next : Rc::new(i32::unsigned_abs)});
+ let f = f.bind(FreeTriv::Pure);
match f {
FreeTriv::Free(f) => {
let n = (f.next)(-4);
@@ -32,6 +34,6 @@ fn test_trivial_with_lifetime(){
FreeTriv::Free(_) => unreachable!(),
}
},
- _ => unreachable!()
+ FreeTriv::Pure(_) => unreachable!()
}
} \ No newline at end of file
diff --git a/tests/vector.rs b/tests/vector.rs
index 74cc913..2b2f4b2 100644
--- a/tests/vector.rs
+++ b/tests/vector.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! Tests if creating a Free Monad for a Vec works. Not sure if this is useful in any way.
//! It is a nice illustration that Free Monads are tree-like though.
@@ -6,117 +8,64 @@ use higher::{Functor, Bind, Apply};
free!(FreeVec<A>, Vec<FreeVec<A>>);
+
+//just to appease clippy without disabling the lint....
+macro_rules! assert_nearly_equal {
+ ($a:expr, $b:expr, $c:expr) => {
+ assert!((($a)-($b)).abs() < $c)
+ };
+}
+
#[test]
fn test_vector(){
- let fv = FreeVec::lift_f(vec![2,3,4]);
- let fv = fv.fmap(|x| x*2);
- let fv = fv.bind(|x| if x%3 == 0 {FreeVec::Pure(x)} else {FreeVec::lift_f(vec![x,x+1])});
- let f = FreeVec::lift_f(vec![(|x| (x as f32) / 3.0) as fn(u32)->f32, (|x| (x+2) as f32) as fn(u32)->f32]);
- let r = fv.apply(f.fmap(Into::into));
- match r {
+ let free_monad = FreeVec::lift_f(vec![2,3,4]);
+ let free_monad_after_fmap = free_monad.fmap(|x| x*2);
+ let free_monad_after_bind = free_monad_after_fmap.bind(|x| if x%3 == 0 {FreeVec::Pure(x)} else {FreeVec::lift_f(vec![x,x+1])});
+ let functions = FreeVec::lift_f(vec![(|x| f64::from(x) / 3.0) as fn(u32)->f64, (|x| f64::from(x+2)) as fn(u32)->f64]);
+ let free_monad_after_apply = free_monad_after_bind.apply(functions.fmap(Into::into));
+ match free_monad_after_apply {
FreeVec::Free(v) => {
match &**v{
- [a,b] => {
- match a {
- FreeVec::Free(v) => {
- match &***v {
- [a,b,c] => {
- match a {
- FreeVec::Free(v) => {
- match &***v {
- [a,b] => {
- match a{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(4.0f32/3.0f32, *v)}
- }
- match b{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(5.0f32/3.0f32, *v)}
- }
- },
- _ => unreachable!()
- }
- }
- FreeVec::Pure(_) => unreachable!(),
- }
- match b {
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => assert_eq!(2.0f32, *v),
- }
- match c {
- FreeVec::Free(v) => {
- match &***v {
- [a,b] => {
- match a{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(8.0f32/3.0f32, *v)}
- }
- match b{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(3.0f32, *v)}
- }
- },
- _ => unreachable!()
- }
- }
- FreeVec::Pure(_) => unreachable!(),
- }
+ [FreeVec::Free(left), FreeVec::Free(right)] => {
+ match &***left {
+ [FreeVec::Free(left), FreeVec::Pure(middle), FreeVec::Free(right)] => {
+ match &***left {
+ [FreeVec::Pure(left), FreeVec::Pure(right)] => {
+ assert_nearly_equal!(4.0f64/3.0f64, *left, f64::EPSILON);
+ assert_nearly_equal!(5.0f64/3.0f64, *right, f64::EPSILON);
+ },
+ _ => unreachable!()
+ }
+ assert_nearly_equal!(2.0f64, *middle, f64::EPSILON);
+ match &***right {
+ [FreeVec::Pure(left),FreeVec::Pure(right)] => {
+ assert_nearly_equal!(8.0f64/3.0f64, *left, f64::EPSILON);
+ assert_nearly_equal!(3.0f64, *right, f64::EPSILON);
},
_ => unreachable!()
}
- }
- FreeVec::Pure(_) => unreachable!()
+ },
+ _ => unreachable!()
}
-
- match b {
- FreeVec::Free(v) => {
- match &***v {
- [a,b,c] => {
- match a {
- FreeVec::Free(v) => {
- match &***v {
- [a,b] => {
- match a{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(6.0f32, *v)}
- }
- match b{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(7.0f32, *v)}
- }
- },
- _ => unreachable!()
- }
- }
- FreeVec::Pure(_) => unreachable!(),
- }
- match b {
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => assert_eq!(8.0f32, *v),
- }
- match c {
- FreeVec::Free(v) => {
- match &***v {
- [a,b] => {
- match a{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(10.0f32, *v)}
- }
- match b{
- FreeVec::Free(_) => unreachable!(),
- FreeVec::Pure(v) => {assert_eq!(11.0f32, *v)}
- }
- },
- _ => unreachable!()
- }
- }
- FreeVec::Pure(_) => unreachable!(),
- }
+ match &***right {
+ [FreeVec::Free(left),FreeVec::Pure(middle),FreeVec::Free(right)] => {
+ match &***left {
+ [FreeVec::Pure(left),FreeVec::Pure(right)] => {
+ assert_nearly_equal!(6.0f64, *left, f64::EPSILON);
+ assert_nearly_equal!(7.0f64, *right, f64::EPSILON);
+ },
+ _ => unreachable!()
+ }
+ assert_nearly_equal!(8.0f64, *middle, f64::EPSILON);
+ match &***right {
+ [FreeVec::Pure(left),FreeVec::Pure(right)] => {
+ assert_nearly_equal!(10.0f64, *left, f64::EPSILON);
+ assert_nearly_equal!(11.0f64, *right, f64::EPSILON);
},
_ => unreachable!()
}
- }
- FreeVec::Pure(_) => unreachable!()
+ },
+ _ => unreachable!()
}
},
_ => unreachable!()
diff --git a/tests/with_lifetimes.rs b/tests/with_lifetimes.rs
index 2e87878..697fc74 100644
--- a/tests/with_lifetimes.rs
+++ b/tests/with_lifetimes.rs
@@ -1,3 +1,5 @@
+#![deny(clippy::pedantic)]
+#![deny(clippy::all)]
//! Test for the case that the Functor the Free Monad is based on has lifetime parameters that do not depend on the
//! lifetime of the mapping function in the Functor implementation.