Props: Gen a, thunks, and closures
In my previous post about Props, I said I would elaborate on its equivalent to Gen a
from Haskell’s QuickCheck.
In Haskell, consider the following implementation of a tree:
data Tree a = Tree { value :: a
, children :: [Tree a]
} deriving (Eq, Show)
Let’s make it an instance of Arbitrary
1:
instance (Arbitrary a) => Arbitrary (Tree a) where
arbitrary = do value <- arbitrary
children <- arbitrary
return Tree { value=value
, children=children
}
Since Haskell can determine types at compile time, we don’t need to put in any type annotations. In particular, we don’t need to tell arbitrary what type we want it to return when making the arbitrary value and children.
In Python, the following class which implements ArbitraryInterface
, would be approximately2 equivalent to the above Haskell type3:
class Tree(ArbitraryInterface):
value = None
children = []
def __init__(self, value, children):
self.value = value
self.children = children
@classmethod
def arbitrary(cls):
return cls(
arbitrary(int), # Value
arbitrary(list_of(Tree)) # Children
)
Ignoring some details4, we can think of Haskell’s Gen a
as representing something which can be turned into an a
. How can we make something that can be turned into an a
? We can use a thunk! A thunk is an object which represents a paused computation. Forcing a thunk causes the computation to occur. Since Haskell is lazy, everything is thunk by default. In Python, iterators can be thought of as lazy, but they usually represent collections of objects, rather than just a single object, so this would not be the best use of iterators.
One simple way to implement a thunk is to make a closure which takes no arguments. For example:
def setup_a_thunk(some, variables, _for, our, computation):
def thunked_computation():
// ...
// Do a bunch of work
// Using the variables passed in to setup_a_thunk
return a_result
return thunked_computation
We can then call setup_a_thunk
a bunch of times, but it would not actually do the work until we force it, i.e. call the thunk with no arguments:
a_thunk = setup_a_thunk(...)
another_thunk = setup_a_thunk(...)
one_result = a_thunk() # This forces the thunk.
Since objects are closures, we can view the class definition for Tree
as setting up a closure, which we force when we call arbitrary
.
So, there you have it. The equivalent of Haskell QuickCheck’s Gen a
in Props is a closure!
This won’t terminate as it would create an infinite Tree structure! But that’s not important here. See StackOverflow if you want a version that would terminate.↩
The Python Tree class is just for
int
. This would be equivalent to theTree Int
type in Haskell. If we had generics in Python… something for a later post.↩This also won’t terminate. I’ll leave that as an exercise for the reader.↩
For example, Haskell’s QuickCheck passes in a nonce to
Gen a
to make sure the generateda
is unique. There’s also the notion ofshrink
which Props does nothing with. Yet.↩