Last active
December 20, 2022 09:02
-
-
Save sebastiaanvisser/cd6ee46356e3ecc89457871ea3ce8cc2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::time::Duration; | |
pub use OpF::*; | |
// Simple operation, with open recursion | |
#[derive(Debug, Clone)] | |
pub enum OpF<R> { | |
Once(String, Duration), | |
Par(Vec<R>), | |
Seq(Vec<R>), | |
Repeat { times: u32, cmd: Box<R> }, | |
} | |
// Tying the knot | |
#[derive(Debug, Clone)] | |
pub struct Op(OpF<Op>); | |
impl<R> OpF<R> { | |
// Functor implementation for OpF | |
pub fn fmap<S, F>(&self, f: F) -> OpF<S> | |
where | |
F: Fn(&R) -> S, | |
{ | |
match self { | |
Once(str, d) => Once(str.clone(), *d), | |
Par(xs) => Par(xs.into_iter().map(f).collect()), | |
Seq(xs) => Seq(xs.into_iter().map(f).collect()), | |
Repeat { times, cmd } => Repeat { | |
times: *times, | |
cmd: Box::new(f(cmd)), | |
}, | |
} | |
} | |
} | |
// We can now put an annotation in the recursive positions! | |
#[derive(Debug, Clone)] | |
pub struct TimedOp { | |
pub duration: Duration, | |
pub op: OpF<TimedOp>, | |
} | |
impl Op { | |
// Cata on operations | |
fn cata<A, F>(&self, f: F) -> A | |
where | |
F: Fn(OpF<A>) -> A + Copy, | |
{ | |
f(self.0.fmap(|x| x.cata(f))) | |
} | |
// Use cata to count number of operations | |
pub fn ops_count(&self) -> u32 { | |
self.cata(|c: OpF<u32>| match c { | |
Once(_, _) => 1, | |
Par(xs) => xs.into_iter().sum(), | |
Seq(xs) => xs.into_iter().sum(), | |
Repeat { times, cmd } => times * (*cmd), | |
}) | |
} | |
// Use cata (endo?) to simulate and track all the timings | |
pub fn simulate(&self) -> TimedOp { | |
self.cata(|op: OpF<TimedOp>| { | |
let duration = match &op { | |
Once(_, d) => *d, | |
Par(xs) => xs.iter().map(|o| o.duration).max().unwrap_or(Duration::ZERO), | |
Seq(xs) => xs.iter().map(|o| o.duration).sum(), | |
Repeat { times, cmd } => cmd.duration * *times, | |
}; | |
TimedOp { duration, op } | |
}) | |
} | |
} | |
#[test] | |
fn test_cata() { | |
let fly = Op(Once("fly".into(), Duration::from_secs(20))); | |
let walk = Op(Once("walk".into(), Duration::from_secs(5))); | |
let both = Op(Par(vec![fly, walk.clone()])); | |
let run = Op(Once("run".into(), Duration::from_secs(10))); | |
let runs = Op(Repeat { | |
times: 5, | |
cmd: Box::new(run), | |
}); | |
let my_op = Op(Seq(vec![both, runs, walk])); | |
assert_eq!(my_op.ops_count(), 8); | |
assert_eq!(my_op.simulate().duration, Duration::from_secs(75)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment