Language Reference
The purpose of this section is to provide an overview of the ReWire language as it relates to vanilla Haskell. Readers unfamiliar with Haskell may wish to consult an introductory text such as Programming in Haskell by Graham Hutton or Learn You a Haskell for Great Good! by Miran Lipovača before diving into ReWire.
ReWire is a subset of Haskell: all ReWire programs are Haskell programs, but not all Haskell programs are ReWire programs. The main difference between ReWire and Haskell is that ReWire places limits on the use of higher order functions, polymorphism, and recursion. Higher order functions and polymorphism are not allowed except where they can be eliminated via inlining. Recursion is only allowed for functions and computations in a certain class of monads, and such computations must be guarded and tail recursive.
Reactive Resumption Monads
The fundamental abstraction in ReWire is a class of monads called reactive resumption monads. Reactive resumption monads allow us to give a functional semantics to clocked, reactive processes like hardware circuits.
While reactive resumption monads are treated as a primitive in ReWire, their semantics can be defined in terms of Haskell. We will start with a simple case, then generalize to a monad transformer. The type of the reactive resumption monad Re
is defined in Haskell as follows.
data Re i o a = Done a
| Pause o (i -> Re i o a)
Think of the type Re i o a
as representing a computation that is exchanging input and output signals (of types i
and o
respectively) with an external environment, and will return a value of type a
if and when it terminates. Formally, a computation in Re i o a
is either in a Done
state, representing a finished computation that has returned a value of type a
, or in a Pause
state, yielding an output of type o
and a function (i.e., a continuation) of type i -> Re i o a
that is waiting for the next input from the environment. The type Re i o
is a monad as follows:
instance Monad (Re i o) where
return = Done
Done v >>= f = f v
Pause o k >>= f = Pause o (k >=> f)
where >=>
is the left-to-right Kleisli composition operator.
We can generalize Re
to a monad transformer ReT
, which allows us to mix other kinds of effects with reactivity.
newtype ReT i o m a =
ReT { deReT :: m (Either a (o, i -> ReT i o m a)) }
instance Monad m => Monad (ReT i o m) where
return = ReT . return . Left
ReT m >>= f = ReT $ do
r <- m
case r of
Left v -> deReT (f v)
Right (o,k) -> return (Right (o,k >=> f))
instance MonadTrans (ReT i o) where
lift m = ReT (m >>= return . Left)
One particularly useful operation in ReT
, which we will actually take as a primitive in ReWire, is called signal
.
signal :: Monad m => o -> ReT i o m i
signal o = ReT (return (Right (o,return)))
Think of signal o
as meaning “yield the output o
to the environment, wait for a new input i
, then return i
”.
Layered State Monads
ReWire also contains built-in support for layered state monads, which enable us to describe circuits with mutable state. Formally, a layered state monad is any monad composed from one or more applications of the state monad transformer StT
to the base identity monad I
. While StT
and I
are primitives in ReWire, they can be defined in Haskell as follows:
newtype StT s m a = StT { deStT :: s -> m (a,s) }
instance Monad m => Monad (StT s m) where
return x = StT $ \ s -> return (x,s)
StT m >>= f = StT $ \ s -> m s >>= \ (x,s) -> deStT (f x) s
instance MonadTrans (StT s) where
lift m = StT $ \ s -> m >>= \ x -> return (x,s)
newtype I a = I { deI :: I a -> a }
instance Monad I where
return = I
I x >>= f = f x
These are, in fact, equivalent to StateT
and Identity
in Haskell’s standard libraries.
The monads supported by ReWire are limited to monads composed of
Restrictions
The ReWire language is essentially a subset of Haskell 98. The major restrictions are as follows.
- Type classes are not (yet) implemented.
- The
type
keyword is not (yet) implemented. - Polymorphic and higher-order functions are only allowed if they can be eliminated via inlining.
- For example, the polymorphic function
fst :: (a,b) -> a
can always be inlined, as can the higher-order (and polymorphic) function(.) :: (b -> c) -> (a -> b) -> (a -> c)
.
- For example, the polymorphic function
Data
types are allowed, including parametric data types likeMaybe
, but only if they are first-order (i.e., do not contain any function- or monad-typed fields) and non-recursive.- Recursive function definitions are allowed, but only in a reactive resumption monad. Such definitions must be tail recursive and guarded.
- FIXME: Insert some examples/nonexamples.
Interfacing with VHDL
- Data types -> bit vectors
nativeVhdl
extrude
start
signal