Difference between revisions of "Netwire"

From HaskellWiki
Jump to: navigation, search
(Writing a wire: A lot of changes.)
(Added subsection Examples and a link to tetris-hs)
 
(50 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Netwire is a library for [[Functional Reactive Programming|functional reactive programming]], which uses the concept of [[Arrow|arrows]] for modelling an embedded domain-specific languageThis 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.
+
Netwire is a [[Functional Reactive Programming|functional reactive programming]] library that provides both an applicative and an arrow interfaceIt allows you to express time-varying values with a rich event system.
  
 +
* [http://hackage.haskell.org/package/netwire Project page]
 +
* [http://hub.darcs.net/ertes/netwire Source repository]
  
 
== Features ==
 
== Features ==
  
Here is a list of some of the features of ''netwire'':
+
Here is a list of some of the features of Netwire:
  
* arrowized interface,
+
* applicative interface (or optionally an arrow interface),
* applicative interface,
+
* signal intervals,
* signal inhibition (''ArrowZero'' / ''Alternative''),
+
* dynamic switching,
* choice and combination (''ArrowPlus'' / ''Alternative''),
+
* rich set of predefined functionality,
* self-adjusting wires (''ArrowChoice''),
+
* signal analysis (average, interpolation, peak, etc.),
* rich set of event wires,
+
* effectful reactive systems.
* signal analysis wires (average, peak, etc.),
 
* impure wires.
 
  
== Quickstart ==
+
== Scope ==
  
This is a quickstart introduction to Netwire for Haskell programmers familiar with arrowized functional reactive programming (AFRP), for example Yampa or Animas.  It should quickly give you an idea of how the library works and how it differs from the two mentioned.
+
Netwire's FRP framework is intended to be used for continuous applications.  It replaces the traditional big main loop with its global state and event callbacks/branching by a completely declarative model.  The following types of applications can benefit from using Netwire:
  
=== The wire ===
+
* artificial intelligence and bots,
 +
* canvas-based graphics and animations,
 +
* continuous signal synthesis (audio waves, etc.),
 +
* games and game servers,
 +
* scene-based user interfaces (like OpenGL and vty),
 +
* simulations.
  
Netwire calls its signal transformation functions ''wires''.  You can think of a wire as a device with an input line and an output line.  The difference between a function and a wire is that a wire can change itself throughout its lifetimeThis is the basic idea of arrowized FRP.  It gives you time-dependent values.
+
If you can sensibly break your application down into ''frames'', then Netwire is for youFor other kinds of reactive applications like widget-based UIs you may want to look into [[reactive-banana]] instead.
  
A wire is parameterized over its input and output types:
 
  
<haskell>
+
== Get started ==
data Wire a b
 
</haskell>
 
  
 +
The documentation is contained within the package itself, but you can also read it online:
  
=== Differences from Yampa ===
+
* [http://hub.darcs.net/ertes/netwire/browse/README.md Tutorial]
 +
* [http://hackage.haskell.org/package/netwire Project page with API docs]
  
If you are not familiar with Yampa or Animas, you can safely skip this section.
 
  
The main difference between Yampa and Netwire is that the underlying arrow is impure.  While you can choose not to use the impure wires inside of the '''FRP.NetWire.IO''' module, it is a design choice for this library to explicitly allow impure computations.  One theoretical implication is that you need to differentiate between pure stateless, pure stateful and impure signal transformations.
+
=== Other reading ===
  
A concept not found in Yampa is signal inhibition.  A wire can choose not to return anything.  This way you can temporarily block entire subnetworks. This is most useful with the combination operator ''<+>''. Example:
+
* [http://todayincode.tumblr.com/post/96914679355/almost-a-netwire-5-tutorial Almost a Netwire 5 Tutorial]
  
<haskell>
 
w = w1 <+> w2
 
</haskell>
 
  
The ''w'' wire runs its signal through the wire ''w1'', and if it inhibits, it passes the signal to ''w2''.
+
=== Examples ===
  
Another concept not found in Yampa is choice.  Through the ''ArrowChoice'' instance wires allow you to choose one of a set of subwires for its signal without needing a switch.  Essentially you can write ''if'' and ''case'' constructs inside of arrow notation.
+
* [https://github.com/ScrambledEggsOnToast/tetris-hs tetris-hs]
  
Because of their impurity wires do not have an ''ArrowLoop'' instance.  It is possible to write one, but it will diverge most of the time, rendering it useless.
 
  
 +
== Model and correctness ==
  
=== Using a wire ===
+
Netwire's underlying abstraction is more powerful than the [http://conal.net/papers/icfp97/ original formulation of time-varying values].  It allows you to implement general component systems with local state.
  
To run a wire you will need to use the ''withWire'' and ''stepWire'' functions.  The ''withWire'' initializes a wire and gives you a ''Session'' value.  As metioned earlier in general a wire is a function, which can mutate itself over time.  The session value captures the current state of the wire.
+
Starting with Netwire 5 there are two module trees <hask>Control.Wire</hask> and <hask>FRP.Netwire</hask>.  The latter exports a subset of Netwire that closely follows the original model and provides a set of predefined FRP wires.
  
<haskell>
+
Signal intervals are an extension of the original model and an integral part of Netwire: A behavior is a ''partial'' function of time with the limitation that both the defined and undefined intervals must have a non-zero length.  This extension makes it much easier to express switching and multicasting systems.
initWire :: Wire a b -> (Session a b -> IO c) -> IO c
 
stepWire :: a -> Session a b -> IO (Maybe b)
 
</haskell>
 
  
The ''stepWire'' function passes the given input value through the wire.  If you use ''stepWire'', then the wire will mutate in real time.  If you need a different rate of time, you can use ''stepWireDelta'' or ''stepWireTime'' instead.
+
== History ==
  
The stepping functions return a ''Maybe b''If the wire inhibits, then the result is ''Nothing'', otherwise it will be ''Just'' the output. Here is a complete example:
+
This project started in 2011 as a replacement for Yampa to provide both a nicer interface and better integration into existing frameworks.  Its original purpose was to power game servers and intelligent network botsThat's the origin of the name ''Netwire''.
  
<haskell>
+
However, before its first release ''signal intervals'' were added (originally under the term ''signal inhibition'').  Netwire became a completely new abstraction, so it lost its connection to Yampa.
{-# LANGUAGE Arrows #-}
 
  
module Main where
+
[[Category:FRP]]
 
 
import Control.Monad
 
import FRP.NetWire
 
import Text.Printf
 
 
 
 
 
myWire :: Wire () String
 
myWire =
 
    proc _ -> do
 
        t <- time -< ()
 
        fps <- avgFps 1000 -< ()
 
        fpsPeak <- highPeak -< fps
 
 
 
        if t < 4
 
          then identity -< "Waiting four seconds."
 
          else identity -<
 
                  printf "Got them! (%8.0f FPS, peak: %8.0f)"
 
                          fps fpsPeak
 
 
 
 
 
main :: IO ()
 
main = withWire myWire loop
 
    where
 
    loop :: Session () String -> IO ()
 
    loop session =
 
        forever $ do
 
            mResult <- stepWire () session
 
            case mResult of
 
              Nothing -> putStr "Signal inhibted."
 
              Just x  -> putStr x
 
            putChar '\r'
 
</haskell>
 
 
 
This program should display the string "Waiting four seconds." for four seconds and then switch to a string, which displays the current average frames per second and peak frames per second.
 
 
 
Note:  Sessions are thread-safe.  You are allowed to use the stepping functions for the same wire from multiple threads.  This makes it easy to implement conditional stepping based on system events.
 
 
 
 
 
=== Writing a wire ===
 
 
 
==== Time ====
 
 
 
To use this library you need to understand the concept of time very well.  Netwire has a continuous time model, which means that when you write your applications you disregard the discrete steps, in which your wire is executed.
 
 
 
Technically at each execution instant (i.e. each time you run ''stepWire'' or one of the other stepping functions) the wire is fed with the input as well as a time delta, which is the time passed since the last instant.  Hence wires do not by themselves keep track of what time it is, since most applications don't need that anyway.  If you need a clock, you can use the predefined ''time'' wire, which will be explained later.
 
 
 
Wires have a local time, which can be different from the global time.  This can happen, when a wire is not actually run, because an earlier wire inhibited the signal.  It also happens, when you use choice.  For example you can easily write a gateway, which repeatedly runs one wire the one second and another wire the other second.  While one wire is run, the other wire is suspended, including its local time.
 
 
 
Local time is a switching effect, which is especially visible, when you use the switching combinators from '''FRP.NetWire.Switch'''.  Local time starts when switching in.
 
 
 
==== Pure stateless wires ====
 
 
 
Pure stateless wires are easy to explain, so let's start with them.  A pure stateless wire is essentially just a function of input.  The simplest wire is the ''identity'' wire.  It just returns its input verbatim:
 
 
 
<haskell>
 
identity :: Wire a a
 
</haskell>
 
 
 
If you run such a wire (see the previous section), then you will just get your input back all the time.  Another simple wire is the ''constant'' wire, which also disregards time:
 
 
 
<haskell>
 
constant :: b -> Wire a b
 
</haskell>
 
 
 
If you run the wire <code>constant 15</code>, you will get as output the number 15 all the time, regardless of the current time and the input.
 
 
 
:'''Note''':  You can express ''identity'' as ''arr id'', but you should prefer ''identity'', because it's faster.  Likewise you can express ''constant x'' as ''arr (const x)'', but again you should prefer ''constant''.
 
 
 
==== Pure stateful wires ====
 
 
 
Let's see a slightly more interesting wire.  The ''time'' wire will return the current local time.  What ''local'' means in this context was explained earlier.
 
 
 
<haskell>
 
time :: Wire a Double
 
</haskell>
 
 
 
As the type suggests, time is measured in seconds and represented as a ''Double''.  The local time starts from 0 at the point, where the wire starts to run.  There is also a wire, which counts time from a different origin:
 
 
 
<haskell>
 
timeFrom :: Double -> Wire a Double
 
</haskell>
 
 
 
The difference between these stateful and the stateless wires from the previous section is that stateful wires mutate themselves over time.  The ''timeFrom x'' wire calculates the current time as ''x'' plus the current time delta.  Let's say that sum is ''y''.  It then mutates into the wire ''timeFrom y''.  As you can see there is no internal clock.  It is really this self-mutation, which gives you a clock.
 

Latest revision as of 21:39, 2 October 2014

Netwire is a functional reactive programming library that provides both an applicative and an arrow interface. It allows you to express time-varying values with a rich event system.

Features

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

  • applicative interface (or optionally an arrow interface),
  • signal intervals,
  • dynamic switching,
  • rich set of predefined functionality,
  • signal analysis (average, interpolation, peak, etc.),
  • effectful reactive systems.

Scope

Netwire's FRP framework is intended to be used for continuous applications. It replaces the traditional big main loop with its global state and event callbacks/branching by a completely declarative model. The following types of applications can benefit from using Netwire:

  • artificial intelligence and bots,
  • canvas-based graphics and animations,
  • continuous signal synthesis (audio waves, etc.),
  • games and game servers,
  • scene-based user interfaces (like OpenGL and vty),
  • simulations.

If you can sensibly break your application down into frames, then Netwire is for you. For other kinds of reactive applications like widget-based UIs you may want to look into reactive-banana instead.


Get started

The documentation is contained within the package itself, but you can also read it online:


Other reading


Examples


Model and correctness

Netwire's underlying abstraction is more powerful than the original formulation of time-varying values. It allows you to implement general component systems with local state.

Starting with Netwire 5 there are two module trees Control.Wire and FRP.Netwire. The latter exports a subset of Netwire that closely follows the original model and provides a set of predefined FRP wires.

Signal intervals are an extension of the original model and an integral part of Netwire: A behavior is a partial function of time with the limitation that both the defined and undefined intervals must have a non-zero length. This extension makes it much easier to express switching and multicasting systems.

History

This project started in 2011 as a replacement for Yampa to provide both a nicer interface and better integration into existing frameworks. Its original purpose was to power game servers and intelligent network bots. That's the origin of the name Netwire.

However, before its first release signal intervals were added (originally under the term signal inhibition). Netwire became a completely new abstraction, so it lost its connection to Yampa.