Skip to content

Commit 6ab9a31

Browse files
committed
update
1 parent b1bab76 commit 6ab9a31

File tree

2 files changed

+69
-24
lines changed

2 files changed

+69
-24
lines changed

io-monad-in-rust/src/cached.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::cell::{Cell, OnceCell};
22

3-
pub struct Cached<T> {
4-
init: Cell<Option<Box<dyn FnOnce() -> T>>>,
3+
pub struct Cached<'a, T> {
4+
init: Cell<Option<Box<dyn FnOnce() -> T + 'a>>>,
55
value: OnceCell<T>,
66
}
77

8-
impl<T> Cached<T> {
9-
pub fn new(init: impl FnOnce() -> T + 'static) -> Self {
8+
impl<'a, T> Cached<'a, T> {
9+
pub fn new(init: impl FnOnce() -> T + 'a) -> Self {
1010
Self {
1111
init: Cell::new(Some(Box::new(init))),
1212
value: OnceCell::new(),
@@ -16,36 +16,36 @@ impl<T> Cached<T> {
1616
pub fn get(&self) -> &T {
1717
self.value.get_or_init(|| {
1818
let Some(init) = self.init.take() else {
19-
panic!("[bug] init function called twice!")
19+
unreachable!("The init function should be never called twice");
2020
};
2121
init()
2222
})
2323
}
24+
25+
pub fn take(self) -> T {
26+
let _ = self.get();
27+
let Some(value) = self.value.into_inner() else {
28+
unreachable!("The value should be always cached after calling get()");
29+
};
30+
value
31+
}
2432
}
2533

2634
#[cfg(test)]
2735
mod tests {
2836
use super::*;
2937

30-
#[derive(Debug)]
31-
struct Foo {
32-
#[allow(dead_code)]
33-
message: String,
34-
}
35-
36-
impl Drop for Foo {
37-
fn drop(&mut self) {
38-
println!("Dropped: {self:?}")
39-
}
40-
}
41-
4238
#[test]
43-
fn test() {
44-
let msg = "hello".to_owned();
45-
let c1 = Cached::new(|| Foo { message: msg });
46-
let msg2 = "hello".to_owned();
47-
let _c2 = Cached::new(|| Foo { message: msg2 });
48-
println!("{:?} world", c1.get());
49-
println!("{:?} world again", c1.get());
39+
fn test_cached() {
40+
let mut n_called = 0;
41+
let cached = Cached::new(|| {
42+
n_called += 1;
43+
"hello".to_string()
44+
});
45+
assert_eq!(cached.get(), "hello");
46+
assert_eq!(cached.get(), "hello");
47+
assert_eq!(cached.get(), "hello");
48+
drop(cached);
49+
assert_eq!(n_called, 1);
5050
}
5151
}

io-monad-in-rust/src/io_monad.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use crate::cached::Cached;
2+
3+
pub struct IO<A>(Cached<'static, A>);
4+
5+
impl<A: 'static> IO<A> {
6+
pub fn pure(a: A) -> IO<A> {
7+
IO(Cached::new(|| a))
8+
}
9+
10+
pub fn map<B>(self, f: impl FnOnce(A) -> B + 'static) -> IO<B> {
11+
IO(Cached::new(move || f(self.0.take())))
12+
}
13+
14+
pub fn apply<B>(self, f: IO<impl FnOnce(A) -> B + 'static>) -> IO<B> {
15+
IO(Cached::new(move || {
16+
let f = f.0.take();
17+
f(self.0.take())
18+
}))
19+
}
20+
21+
pub fn bind<B>(self, f: impl FnOnce(A) -> IO<B> + 'static) -> IO<B> {
22+
IO(Cached::new(move || {
23+
let x = f(self.0.take());
24+
x.0.take()
25+
}))
26+
}
27+
}
28+
29+
#[cfg(test)]
30+
mod tests {
31+
use crate::cached::Cached;
32+
33+
use super::IO;
34+
35+
fn put_str_ln(s: String) -> IO<()> {
36+
IO(Cached::new(move || println!("{s}")))
37+
}
38+
39+
#[test]
40+
fn test() {
41+
let action1 = IO::pure("hello".to_string());
42+
let action2 = action1.bind(|s| put_str_ln(s));
43+
action2.0.take();
44+
}
45+
}

0 commit comments

Comments
 (0)