Safe Haskell | None |
---|---|
Language | Haskell2010 |
This module implements the core "trick" of StrictCheck: observing the demand behavior of a function in a purely functional way.
All the functions in this module are safe and referentially transparent.
Observing the evaluation of a function using these functions incurs at most a small constant multiple of overhead compared to just executing the function with no observation.
- observe1 :: (Shaped a, Shaped b) => (b -> ()) -> (a -> b) -> a -> (Demand b, Demand a)
- observe :: (All Shaped (Args function), Shaped (Result function), Curry (Args function)) => (Result function -> ()) -> function -> Args function ⋯-> (Demand (Result function), NP Demand (Args function))
- observeNP :: (All Shaped inputs, Shaped result) => (result -> ()) -> (NP I inputs -> result) -> NP I inputs -> (Demand result, NP Demand inputs)
Documentation
observe1 :: (Shaped a, Shaped b) => (b -> ()) -> (a -> b) -> a -> (Demand b, Demand a) Source #
Observe the demand behavior
- in a given evaluation context,
- of a given unary function,
- called upon a given input,
returning a pair of
- the demand on its output exerted by the evaluation context, and
- the demand on its input this induced
Suppose we want to see how strict reverse
is when we evaluate its result
to weak-head normal form:
>>>
(b, a) = observe1 (`seq` ()) (reverse @Int) [1, 2, 3]
>>>
printDemand b -- output demand
_ : _>>>
printDemand a -- input demand
_ : _ : _ : _ : []
This tells us that our context did indeed evaluate the result of reverse
to force only its first constructor, and that doing so required the entire
spine of the list to be evaluated, but did not evaluate any of its elements.
observe :: (All Shaped (Args function), Shaped (Result function), Curry (Args function)) => (Result function -> ()) -> function -> Args function ⋯-> (Demand (Result function), NP Demand (Args function)) Source #
Observe the demand behavior
- in a given evaluation context
- of a given curried n-ary function
- called upon all of its inputs (provided as curried ordinary inputs),
returning a pair of
- the demand on its output exerted by the evaluation context, and
- the demands on its inputs this induced, represented as an
NP
Demand
from Generics.SOP
This function is variadic and curried: it takes n + 2
arguments, where
n
is the total number of arguments taken by the observed function.
Suppose we want to see how strict zipWith (*)
is when we evaluate its
result completely (to normal form):
>>>
productZip = zipWith ((*) @Int)
>>>
(zs, (xs :* ys :* Nil)) = observe normalize productZip [10, 20] [30, 40]
>>>
printDemand zs -- output demand
300 : 800 : []>>>
printDemand xs -- input demand #1
10 : 20 : []>>>
printDemand ys -- input demand #2
30 : 40 : _
If you haven't thought very carefully about the strictness behavior of zip
,
this may be a surprising result; this is part of the fun!
observeNP :: (All Shaped inputs, Shaped result) => (result -> ()) -> (NP I inputs -> result) -> NP I inputs -> (Demand result, NP Demand inputs) Source #
Observe the demand behavior
- in a given evaluation context
- of a given uncurried n-ary function (taking as input an n-ary
product of inputs represented as an
NP
I
from Generics.SOP) - called upon all of its inputs (provided as curried ordinary inputs),
returning a pair of
- the demand on its output exerted by the evaluation context, and
- the demands on its inputs this induced, represented as an
NP
Demand
from Generics.SOP
This is mostly useful for implementing the internals of StrictCheck;
observe
is more ergonomic for exploration by end-users.