purescript-call-by-name
Syntactically light-weight call-by-name arguments in PureScript. No guarantees. Completely gratuitous.
What is this library?
This library takes advantage of term-abstraction incurred by type class
dictionaries to approximate call-by-name evaluation for arguments. We
simulate call-by-name by taking a Unit -> a
argument which defers evaluation
of a
until invoked. We then provide operators to coerce these
type-class-abstracted terms into the more reliable call-by-name form.
Take the when
function from Prelude
:
when :: forall m. Applicative m => Boolean -> m Unit -> m Unit
It is strict in both arguments, so it will allocate the provided effect
even though it may be discarded. This can be problematic, especially if there
are let
bound values:
example =
when that do
let
a = somethingExpensive 42
b = somethingElseExpensive a
this a b
Both a
and b
will be evaluated, which we don't want. The alternative is to
use a call-by-name approximation:
when :: forall m. Applicative m => Boolean -> (Unit -> m Unit) -> m Unit
example =
when that \_ -> do
let
a = somethingExpensive 42
b = somethingElseExpensive a
this a b
But wow that's irritating. This library exports an application operator \\
that makes this look better.
import CallByName.Applicative (when)
import CallByName.Syntax ((\\))
example =
when that \\do
let
a = somethingExpensive 42
b = somethingElseExpensive a
this a b
We've saved four arduous characters.
There's also the ~
operator which lifts terms into Lazy
.
somethingLazy :: Lazy Int -> Int
example =
somethingLazy ~(evaluate expensive int)
Versus the old:
example =
somethingLazy (Lazy.defer \_ -> evaluate expensive int)
Somewhat actually useful things
This library also exports some call-by-name variants of functions that don't exist in current Prelude or ecosystem.
CallByName.Applicative.when ::
forall m. Applicative m => Boolean -> (Unit -> m Unit) -> m Unit
CallByName.Applicative.unless ::
forall m. Applicative m => Boolean -> (Unit -> m Unit) -> m Unit
CallByName.Monoid.guard ::
forall m. Monoid m => Boolean -> (Unit -> m) -> m
And also exports a version of the Alt
type class which defers it's second
argument.
class Strict.Alt f <= Alt f where
alt :: forall a. f a -> (Unit -> f a) -> f a
This can be combined with a magic, right-associated version of <|>
which
does not strictly evaluate it's second argument.
example =
Just 42 <|> unsafeThrow "Too strict!"