Difference between revisions of "Netwire"

From HaskellWiki
Jump to navigation Jump to search
(Updated to Netwire 3.0.0 – incomplete)
Line 19: Line 19:
 
== Basics ==
 
== Basics ==
   
The Netwire library is based around a data type called <hask>Wire</hask>:
+
The Netwire library is based around a data type called <hask>Wire</hask>. You need to import the <hask>Control.Wire</hask> module to work with wires:
   
 
<haskell>
 
<haskell>
  +
import Control.Wire
  +
 
data Wire e (>~) a b
 
data Wire e (>~) a b
 
</haskell>
 
</haskell>
   
For some arrows <hask>(>~)</hask> and all monoids <hask>e</hask> the type <hask>Wire e (>~)</hask> is an arrow. Only certain arrows are allowed for <hask>(>~)</hask>, because ''Wire'' is actually a data family. These arrows are called base arrows in Netwire.
+
For some arrows <hask>(>~)</hask> and all monoids <hask>e</hask> the type <hask>Wire e (>~)</hask> is an arrow. Only certain arrows are allowed for <hask>(>~)</hask>, because <hask>Wire</hask> is actually a data family. These arrows are called base arrows in Netwire.
   
 
<haskell>
 
<haskell>
Line 39: Line 41:
 
So it's a function that takes a value of type <hask>a</hask> and either produces a value of type <hask>b</hask> or produces no value, but instead ''inhibits'' with a value of type <hask>e</hask>. The act of running a wire is called ''stepping'' and the process is called an ''instant''. You can step a wire through one of the stepping functions, which we will cover later. When you step a wire, it will return a new version of itself along with its result. You are supposed to call the new version the next time you step.
 
So it's a function that takes a value of type <hask>a</hask> and either produces a value of type <hask>b</hask> or produces no value, but instead ''inhibits'' with a value of type <hask>e</hask>. The act of running a wire is called ''stepping'' and the process is called an ''instant''. You can step a wire through one of the stepping functions, which we will cover later. When you step a wire, it will return a new version of itself along with its result. You are supposed to call the new version the next time you step.
   
  +
=== The inhibition monoid ===
=== Base arrows
 
   
The <hask>(>~)</hask> argument to <hask>Wire</hask> is called the base arrow.
+
The <hask>e</hask> argument to <hask>Wire</hask> is called the inhibition monoid. For simple applications you can just use <hask>()</hask> here, but you may want to actually assign exception values to inhibition. We will cover that later. For now just use <hask>()</hask>.
  +
 
=== Base arrows ===
  +
  +
The <hask>(>~)</hask> argument to <hask>Wire</hask> is called the base arrow. In most cases you will use a <hask>Kleisli</hask> arrow here, and this is currently the only type of arrow supported, though more will be added in the future. For simple applications you can just use the <hask>IO</hask> monad, and it is useful to define a type alias for your custom wire type:
  +
  +
<haskell>
  +
type MyWire = Wire () (Kleisli IO)
  +
</haskell>
  +
  +
== Running wires ==
  +
  +
For running a wire you can use the stepping functions available in the <hask>Control.Wire.Session</hask> module. There is no need to import that module. It is automatically imported with <hask>Control.Wire</hask>. For Kleisli-based wires you will want to use the <hask>stepWireM</hask> function:
  +
  +
<haskell>
  +
stepWireM ::
  +
Monad m
  +
=> Wire e (Kleisli m) a b
  +
-> a
  +
-> m (Either e b, Wire e (Kleisli m) a b)
  +
</haskell>
  +
  +
In our case we have <hask>m = IO</hask>, so our type signature is simply:
  +
  +
<haskell>
  +
stepWireM :: MyWire a b -> a -> IO (Either () b, MyWire a b)
  +
</haskell>
  +
  +
This function takes a wire and an input value. It passes the input value to the wire and returns its result value of type <hask>Either () b</hask>. Along with the result it also returns a new wire. Normally you would call <hask>stepWireM</hask> in a loop, which performs instant after instant. This is the basic structure:
  +
  +
<haskell>
  +
system :: MyWire Int String
  +
system = {- ... -}
  +
  +
main :: IO ()
  +
main = loop system
  +
where
  +
loop :: MyWire Int String -> IO ()
  +
loop w' = do
  +
(mx, w) <- stepWireM w' 15
  +
  +
{- ... do something with mx ... -}
  +
  +
loop w -- loop with the new wire.
  +
</haskell>
   
 
[[Category:FRP]]
 
[[Category:FRP]]

Revision as of 16:00, 1 December 2011

Netwire is a library for functional reactive programming, which uses the concept of arrows for modelling an embedded domain-specific language. This language lets you express reactive systems, which means systems that change over time. It shares the basic concept with Yampa and its fork Animas, but it is itself not a fork and has many additional features.

This wiki page corresponds to Netwire version 3 and is currently a work in progress.

Features

Here is a list of some of the features of Netwire:

  • arrow interface (or optionally an applicative interface),
  • signal inhibition (ArrowZero / Alternative),
  • signal selection (ArrowPlus / Alternative),
  • self-adjusting wires (ArrowChoice),
  • rich set of event wires,
  • signal analysis wires (average, peak, etc.),
  • effectful wires.

Basics

The Netwire library is based around a data type called Wire. You need to import the Control.Wire module to work with wires:

import Control.Wire

data Wire e (>~) a b

For some arrows (>~) and all monoids e the type Wire e (>~) is an arrow. Only certain arrows are allowed for (>~), because Wire is actually a data family. These arrows are called base arrows in Netwire.

comp :: Wire e (>~) a b

Values of type Wire e (>~) a b are time-varying functions, which resemble the following type:

a >~ Either e b

So it's a function that takes a value of type a and either produces a value of type b or produces no value, but instead inhibits with a value of type e. The act of running a wire is called stepping and the process is called an instant. You can step a wire through one of the stepping functions, which we will cover later. When you step a wire, it will return a new version of itself along with its result. You are supposed to call the new version the next time you step.

The inhibition monoid

The e argument to Wire is called the inhibition monoid. For simple applications you can just use () here, but you may want to actually assign exception values to inhibition. We will cover that later. For now just use ().

Base arrows

The (>~) argument to Wire is called the base arrow. In most cases you will use a Kleisli arrow here, and this is currently the only type of arrow supported, though more will be added in the future. For simple applications you can just use the IO monad, and it is useful to define a type alias for your custom wire type:

type MyWire = Wire () (Kleisli IO)

Running wires

For running a wire you can use the stepping functions available in the Control.Wire.Session module. There is no need to import that module. It is automatically imported with Control.Wire. For Kleisli-based wires you will want to use the stepWireM function:

stepWireM ::
    Monad m
    => Wire e (Kleisli m) a b
    -> a
    -> m (Either e b, Wire e (Kleisli m) a b)

In our case we have m = IO, so our type signature is simply:

stepWireM :: MyWire a b -> a -> IO (Either () b, MyWire a b)

This function takes a wire and an input value. It passes the input value to the wire and returns its result value of type Either () b. Along with the result it also returns a new wire. Normally you would call stepWireM in a loop, which performs instant after instant. This is the basic structure:

system :: MyWire Int String
system = {- ... -}

main :: IO ()
main = loop system
    where
    loop :: MyWire Int String -> IO ()
    loop w' = do
        (mx, w) <- stepWireM w' 15

        {- ... do something with mx ... -}

        loop w  -- loop with the new wire.