Towards Haskell in the Cloud (Epstein et al, Haskell Symposium 2011)
introduces the concept of static values: values that are known at compile
time. In a distributed setting where all nodes are running the same
executable, static values can be serialized simply by transmitting a code
pointer to the value. This however requires special compiler support, which
is not yet available in ghc. We can mimick the behaviour by keeping an
explicit mapping (RemoteTable) from labels to values (and making sure that
all distributed nodes are using the same RemoteTable). In this module
we implement this mimickry and various extensions.

Dynamic type checking

The paper stipulates that Static values should have a free Binary
instance:

instance Binary (Static a)

This however is not (runtime) type safe: for instance, what would be the
behaviour of

f :: Static Int -> Static Bool
f = decode . encode

For this reason we work only with Typeable terms in this module, and
implement runtime checks

instance Typeable a => Binary (Static a)

The above function f typechecks but throws an exception if executed. The
type representation we use, however, is not the standard
TypeRep from Data.Typeable but
TypeRep from Data.Rank1Typeable. This means that we
can represent polymorphic static values (see below for an example).

Since the runtime mapping (RemoteTable) contains values of different types,
it maps labels (Strings) to Dynamic values. Again, we
use the implementation from Data.Rank1Dynamic so that we can store
polymorphic dynamic values.

Compositionality

Static values as described in the paper are not compositional: there is no
way to combine two static values and get a static value out of it. This
makes sense when interpreting static strictly as known at compile time,
but it severely limits expressiveness. However, the main motivation for
static is not that they are known at compile time but rather that
they provide a freeBinaryinstance. We therefore provide two basic
constructors for Static values:

Closures in functional programming arise when we partially apply a function.
A closure is a code pointer together with a runtime data structure that
represents the value of the free variables of the function. A Closure
represents these closures explicitly so that they can be serialized:

Suppose we are working in the context of some distributed environment, with
a monadic type Process representing processes, NodeId representing node
addresses and ProcessId representing process addresses. Suppose further
that we have a primitive

sendInt :: ProcessId -> Int -> Process ()

We might want to define

sendIntClosure :: ProcessId -> Closure (Int -> Process ())

In order to do that, we need a static version of send, and a static
decoder for ProcessId:

In the above we were careful to avoid qualified types. Suppose that we have
instead

send :: Binary a => ProcessId -> a -> Process ()

If we now want to define sendClosure, analogous to sendIntClosure above,
we somehow need to include the Binary instance in the closure -- after
all, we can ship this closure someplace else, where it needs to accept an
a, then encode it, and send it off. In order to do this, we need to turn
the Binary instance into an explicit dictionary: