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 => Pattern -> a -> b -> b
- forcep :: NFDataP a => Pattern -> a -> a
- deepseqp_ :: NFDataP a => Pattern -> a -> b -> b
- forcep_ :: NFDataP a => Pattern -> a -> a
- data DeepSeqBounded_PingException = DeepSeqBounded_PingException String
- module Control.DeepSeq.Bounded.Pattern
- module Control.DeepSeq.Bounded.PatUtil
- class (Typeable a, NFDataN a, NFData a) => NFDataP a where
- handleAttrs :: forall d. Typeable d => Pattern -> d -> Pattern
Documentation
deepseqp :: NFDataP a => Pattern -> 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).
Self-composition fuses via
"deepseqp/composition" forall p1 p2 x1 x2. (.) (deepseqp
p2 x2) (deepseqp
p1 x1) =deepseqp
(liftPats
[p1, p2] ) (x1,x2)
(Other fusion rules, not yet documented, may also be in effect.)
forcep :: NFDataP a => Pattern -> 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."
Self-composition fuses via
"forcep/composition" forall p1 p2 x. (.) (forcep
p2) (forcep
p1) x =forcep
(unionPats
[p1, p2] ) x
(Other fusion rules, not yet documented, may also be in effect.)
deepseqp_ :: NFDataP a => Pattern -> a -> b -> b Source
Deprecated: OverloadedStrings is in effect for pattern strings (since 0.8), so you can use deepseqp and forcep with either a Pattern or a String literal as first argument. To work with String variables, write your own wrapper function calling compilePat (or build with OVERLOEADED_STRINGS flag False). These underscore versions will be removed in 0.9 (mere days from now...).
forcep_ :: NFDataP a => Pattern -> a -> a Source
Deprecated: OverloadedStrings is in effect for pattern strings (since 0.8), so you can use deepseqp and forcep with either a Pattern or a String literal as first argument. To work with String variables, write your own wrapper function calling compilePat (or build with OVERLOEADED_STRINGS flag False). These underscore versions will be removed in 0.9 (mere days from now...).
deepseqp_
and forcep_
are merely aliases to the
non-underscored functions. They are vestigial and
retained for compatibility until the next major
version bump. Then it seems safe to remove them,
since can always build with OVERLOADED_STRINGS
flag
set False
if their absence is a problem (or define
the aliases yourself). So, DEPRECATED, and slated
for removal in 0.8.
A custom exception, raised by choice PatNode
s, that can be caught in the caller
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
Shared with GNFDataP (internal use)
handleAttrs :: forall d. Typeable d => Pattern -> d -> Pattern Source