BurntSushi/quickcheck

implement Testable for Fn*

abr-egn opened this issue · 13 comments

Using rustc 1.0.0-nightly (3d0d9bb6f 2015-01-12 22:56:20 +0000):

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec!();
    for x in xs.iter() {
        rev.insert(0, x.clone())
    }
    rev
}

#[test]
fn reverse_identity() {
  fn prop(xs: Vec<i32>) -> bool {
    xs == reverse(reverse(xs.as_slice()).as_slice())
  }
  quickcheck(prop);
}

produces

error: the trait `quickcheck::tester::Testable` is not implemented for the type `fn(collections::vec::Vec<i32>) -> bool {tests::reverse_identity::prop}`

quickcheck(false) compiles; this appears to be specific to the fn impls, including the zero-arg fn.

Errm, try: quickcheck(prop as fn(Vec<i32>) -> bool).

That does work, thank you. I'm a bit baffled that it's required since it's trivially inferrable type information...

I'm pretty sure it's a known bug, but I can't find it in the Rust issue tracker at the moment. I think this happened when Rust moved everything over to unboxed closures. Unfortunately, I can't remember why this happens.

The PR that introduced this, I think, is rust-lang/rust#19891. I too remember seeing something that indicated this coercion is supposed to exist at some point but can't find it either.

Afaik, functions now implement the Fn(…) -> … traits. So I think it would actually be cleaner if quickcheck would impl<F: Fn(…) -> …> Testable for F instead of coercing to a trait object.

Edit: that does not work together with the coherence rules, does it?

I've tried really hard to work around this (by pursuing impls with Fn instead of fn) and I haven't been able to come up with a solution yet.

I think I'm currently blocked on this by this bug: rust-lang/rust#25041 (Well, I think it's a bug. It sure feels like it.)

cc me

Just to clarify: we are currently blocked on being able to quickcheck an arbitrary closure? (Important to be able to nest to stage generation of values, as in Haskell QuickCheck and Scala ScalaCheck.)

@FranklinChen Yup. I might be missing another solution to the problem, but the straight-forward path is to implement Testable on closure types, and the bug in rust-lang/rust#25041 seems to be preventing that.

While this still doesn't seem possible at the type level yet, you can reduce a bit of boilerplate by using _ when casting to the function pointer:

fn prop(xs: Vec<i32>) -> bool {
    xs == reverse(&reverse(&xs[..])[..])
}
quickcheck(prop as fn(_) -> _);

This is a bit nicer as you only have to duplicate the number of arguments, not the types.

@shepmaster Ah, thanks for that tip. Could a macro automate that?

As far as I can tell, this is just never going to work. See rust-lang/rust#25041 for more discussion, where it seems to me the issue has been interpreted as "confusing" rather than a bug to fix. Some reasons are given but I don't quite understand them. I don't see any movement to resolve it, so after half a decade, I'm giving up and calling this wontfix.

I would very much love to make quickcheck work with closures though. So if there are any fresh ideas, we can re-open is this issue.