Copyright | Andrew G. Seniuk 2014-2015 |
---|---|
License | BSD-style (see the file LICENSE) |
Maintainer | Andrew Seniuk <rasfar@gmail.com> |
Stability | provisional |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides an overloaded function, deepseqp
, for partially
(or fully) evaluating data structures to bounded depth via pattern
matching on term shape, and on class, type, and constructor names.
There are two ways to use this API.
- You can use the
PatNode
constructors directly. - You can compile your patterns from strings in a concise embedded language.
There's no difference in expressive power, but use of the DSL
is recommended, because the embedded Pattern
compiler can catch
some errors that GHC cannot (using PatNode
constructors explicitly).
Also, the pattern strings are easier to read and write.
With some qualifications (concerning WI
nodes, and PatNodeAttrs
configuration), composition fuses, and what's more, it's commutative...
Motivation
A typical use is to ensure any exceptions hidden within lazy fields of a data structure do not leak outside the scope of the exception handler; another is to force evaluation of a data structure in one thread, before passing it to another thread (preventing work moving to the wrong threads). Unlike DeepSeq, potentially infinite values of coinductive data types are supported by principled bounding of deep evaluation.
It is also useful for diagnostic purposes when trying to understand
and manipulate space/time trade-offs in lazy code,
and as an optimal substitute for deepseq
(where "optimal" doesn't include changing the code to remove
the need for artificial forcing!).
deepseqp
with optimal patterns is usually a better solution
even than stict fields in your data structures, because the
latter will behave strictly everywhere the constructors
are used, instead of just where its laziness is problematic.
There may be possible applications to the prevention of resource leaks in lazy streaming, but I'm not certain.
Semantics
(For additional details, see Control.DeepSeq.Bounded.Pattern.)
deepseqp
and friends artifically force evaluation of a term
so long as the pattern matches.
A mismatch occurs at a pattern node when the corresponding constructor node either:
- has arity different than the number of subpatterns (only when subpatterns given)
- has class/type/name not named in the constraint (only when constraint given)
A mismatch will cause evaluation down that branch to stop, but any
upstream matching/forcing will continue uninterrupted.
(This behaviour can now be changed with PatNodeAttrs
, available since 0.6.)
Note that patterns may extend beyond the values they match against,
without incurring any mismatch. This semantics is not the only
possible, but bear in mind that order of evaluation is nondeterministic,
barring further measures.
(This behaviour can also now be changed with PatNodeAttrs
.)
See also NFDataPDyn for another approach, which dynamically generates forcing patterns, and can depend on value info (in addition to type info). (These dynamic aspects never received the attention I intended to give them, I got so caught up in seqaid, which offers similar features. Hopefully actual use of these tools in the near future will give me some perspective on whether NFDataPDyn should get attention.)
- deepseqp :: NFDataP a => String -> a -> b -> b
- forcep :: NFDataP a => String -> a -> a
- deepseqp_ :: NFDataP a => Pattern -> a -> b -> b
- forcep_ :: NFDataP a => Pattern -> a -> a
- data DeepSeqBounded_PingException = DeepSeqBounded_PingException String
- handleAttrs :: forall d. Typeable d => Pattern -> d -> Pattern
- module Control.DeepSeq.Bounded.Pattern
- module Control.DeepSeq.Bounded.PatUtil
- class (Typeable a, NFDataN a, NFData a) => NFDataP a where
Pattern-bounded analogues of deepseq
and force
deepseqp :: NFDataP a => String -> a -> b -> b Source
deepseqp
evaluates the first argument to the extent specified
by a Pattern
, before returning the second.
Quoting from the DeepSeq documentation (deepseq package):
"deepseq can be useful for forcing pending exceptions, eradicating space leaks, or forcing lazy I/O to happen. It is also useful in conjunction with parallel Strategies (see the parallel package).
There is no guarantee about the ordering of evaluation. The implementation may evaluate the components of the structure in any order or in parallel. To impose an actual order on evaluation, use pseq
from Control.Parallel in the parallel
package."
forcep :: NFDataP a => String -> a -> a Source
A variant of deepseqp
that is sometimes convenient:
forcep pat x = deepseqp pat x x -- (cannot write x `deepseqp pat` x by analogy with x `deepseq` x)
forcep pat x
evaluates x
to the depth determined by pat
, and
then returns x
. Again from
deepseq:
"Note that forcep pat x
only takes effect when the value of forcep pat x
itself is demanded, so essentially it turns shallow evaluation into evaluation to arbitrary bounded depth."
Composition fuses (see forcep_
).
Avoid DSL compilation overhead
A custom exception, raised by choice PatNode
s, that can be caught in the caller.
handleAttrs :: forall d. Typeable d => Pattern -> d -> Pattern Source
Related modules re-exported
Class of things that can be evaluated over an arbitrary finite pattern
class (Typeable a, NFDataN a, NFData a) => NFDataP a where Source
A class of types that can be evaluated over an arbitrary finite pattern.
Nothing