jeudi 29 septembre 2016

Easily getting function names in F#

While searching for an answer, I've found several questions that don't quite match my situation — so I'll ask a new one.

I am writing some FsCheck tests for a data structure, where I want to check about twenty different properties on each copy of the data structure I construct. What I've done so far is to write a predicate for each property, then I've made a list of the predicates and I'll call them each in turn with List.forall, like so:

module PropertyChecks =
    let ``Tail length plus tree length should equal vector length`` vec =
        treeLength vec.root + Array.length vec.tail = vec.len

    let ``The tail may only be empty iff the vector is empty`` vec =
        (vec.len = 0) = (Array.length vec.tail = 0)

    let ``The tail's length may not exceed tailMax`` vec =
        Array.length vec.tail < tailMax

    let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
        (vec.len > tailMax) || (Array.length vec.root = 0)

    // And so on for about 15 more predicates

    let allProperties =
      [
        ``Tail length plus tree length should equal vector length``
        ``The tail may only be empty iff the vector is empty``
        ``The tail's length may not exceed tailMax``
        ``If vec.len <= tailMax, all items are in tail and root is empty``
      ]

    let checkProperties vec =
        allProperties |> List.forall (fun pred -> pred vec)

    // Rest of my code omitted since it's not relevant to the question

The problem I'm facing is that I expect that when one property fails because I didn't construct the data structure properly, two or three other properties will fail at the same time. I'd like to get a list of all failing properties, which means that in checkProperties I'd like to extract the name of the failing predicate. I've seen multiple answers along the lines of "You can't get the MethodInfo from an arbitrary F# function that you received as an argument, because you never know whether you got the function itself, or a lambda, or an anonymous function". But here, I not only know that I have real functions, I know what their names are. I could easily copy and paste their names into strings, and make allProperties a list of (string,function) tuples. But I'm already feeling not quite happy with having copied-and-pasted once (to put the predicates into that list), and I'd rather not do it twice.

What's a better solution that making a list of (function name, function) tuples? Could I move allProperties and checkProperties out of the PropertyChecks module so that it contains nothing but predicates, and then use reflection to walk that module?

I'm very new to the entire .Net reflection system, so I might well be missing something obvious. Please feel free to point out the obvious if I'm missing it; I won't feel insulted.





Aucun commentaire:

Enregistrer un commentaire