Skip to content

Instantly share code, notes, and snippets.

@sebastiaanvisser
Last active December 20, 2022 09:02
Show Gist options
  • Save sebastiaanvisser/cd6ee46356e3ecc89457871ea3ce8cc2 to your computer and use it in GitHub Desktop.
Save sebastiaanvisser/cd6ee46356e3ecc89457871ea3ce8cc2 to your computer and use it in GitHub Desktop.
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