The concept of this project is to implement everything using only lambda. In Rust, the implementation is to wrap lambda, that is Fn type, into Rc and Enum Pol. Recently I just start to think whether we can print out the Pol we defined. I try to implement Debug for it, but soon I stuck into Fn type in the Pol, that prevents me to print it.
Here is my implementation:
impl fmt::Debug for Pol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Pol::C(ref c) => write!(f, "p {:?} {{}}", c), // stuck here, c cannot be print
&Pol::I(ref i) => write!(f, "{}", i),
&Pol::B(ref b) => write!(f, "{}", b),
}
}
}
The c in Pol::C will have type std::ops::Fn(std::rc::Rc<pol::Pol>) -> std::rc::Rc<pol::Pol>. I tried to
implement Debug for it:
impl fmt::Debug for Fn(Rc<Pol>) -> Rc<Pol> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt: Result {
write!(f, "")
}
}
The compiler will complain that Debug, and Fn are not from current crate neither, and refuse my implementation. So, just ask that, is it possible to print out Pol type under this condition?
You can get around the coherence issue by making your own trait:
// (note: I'm calling this PolFnTr so that the better name PolFn can be
// given to the next item)
pub trait PolFnTr: Fn(Rc<Pol>) -> Rc<Pol> { }
impl<F> PolFnTr for F where F: Fn(Rc<Pol>) -> Rc<Pol> { }
// When you try to use PolFnTr directly as a trait object, rust will complain that
// the associated type `Fn::Output` is not specified. This is a workaround:
/// Type alias for PolFnTr trait objects.
pub type PolFn = PolFnTr<Output = Rc<Pol>>;
impl fmt::Debug for PolFn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{ write!(f, "PolFn") }
}
After that, you would use PolFn in places where you currently use Fn(Rc<Pol>) -> Rc<Pol>.
That said, I'm not sure what you're trying to achieve by giving it a Debug impl. If it is just to make #[derive(Debug)] easier, the above should suffice, but from the looks of your code above it seems you actually want to get it to print something useful.
Are you perhaps hoping that it would print the body of the closure? That won't be possible unless you replace the Fn trait objects with your own type that stores the string to be printed. (which I think can be done with a macro; I'll sketch something out if you need me to)
/// Extends a (possibly unsized) value with a Debug string.
// (This type is unsized when T is unsized)
pub struct Debuggable<T: ?Sized> {
text: &'static str,
value: T,
}
/// Produce a Debuggable<T> from an expression for T
macro_rules! dbg {
($($body:tt)+) => {
Debuggable {
text: stringify!($($body)+),
value: $($body)+,
}
};
}
// Note: this type is unsized
pub type PolFn = Debuggable<Fn(Rc<Pol>) -> Rc<Pol>>;
impl<T: ?Sized> fmt::Debug for Debuggable<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{ write!(f, "{}", self.text) }
}
// This makes Debuggable have most methods of the thing it wraps.
// It also lets you call it when T is a function.
impl<T: ?Sized> ::std::ops::Deref for Debuggable<T>
{
type Target = T;
fn deref(&self) -> &T { &self.value }
}
fn main() {
let d: &PolFn = &dbg!(|x| {
let _ = "random code so you can see how it's formatted";
assert_eq!(3 * (1 + 2), 9);
x
});
println!("{:?}", d);
}
Updated 1/09:@juggle-tux has pointed out these objects can actually be called using standard function-call syntax (I didn't realize Deref could do that!), so I removed the call method.
In Pol you would store Rc<PolFn>. In your code you will need to wrap dbg!() around a lot of your closures.
In the playground this prints:
| x | {
let _ = "random code so you can see how it's formatted" ; assert_eq ! (
3 * ( 1 + 2 ) , 9 ) ; x }
as you can see, the formatting is not spectacular, but that's because all the whitespace gets normalized away during tokenization. I don't think you can get much better without resorting to crazy hacks like preprocessing your code in a build script.
My motivation is to print out the detail structure of composite lambda (though meaningless, just for fun). In the book it prints out the complete fizzbuzz program in lambda, with the help from Ruby, and I think it may be possible to do it in Rust.
If there is reference of variable outside of closure, how to make this method still work?
such as
fn main() {
let v = 10;
let d: &PolFn = &dbg!(|x: usize| {
let _ = "random code so you can see how it's formatted";
assert_eq!(3 * (1 + 2), 9);
if x > v {x} else {x * 2}
});
println!("{:?}", d);
}
I test the code above,
| x | {
let _ = "random code so you can see how it's formatted" ; assert_eq ! (
3 * ( 1 + 2 ) , 9 ) ; if x > v {x} else {x * 2} }
but how to make the variable 'v' can be print as its actual value, please help, thanks. @ExpHP