Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting input into Netwire programs

I'm getting started with Netwire version 5.

I have no problem writing all the wires I want to transform my inputs into my outputs.

Now the time has come to write the IO wrapper to tie in my real-world inputs, and I am a bit confused.

Am I supposed to create a custom session type for the s parameter of Wire s e m a b and embed my sensor values in there?

If so, I have these questions:

  1. What's up with the Monoid s context of class (Monoid s, Real t) => HasTime t s | s -> t? What is it used for?
  2. I was thinking of tacking on a Map String Double with my sensor readings, but how should my monoid crunch the dictionaries? Should it be left-biased? Right-biased? None of the above?

If not, what am I supposed to do? I want to end up with wires of the form Wire s InhibitionReason Identity () Double for some s, representing my input.

It's my understanding that I don't want or need to use the monadic m parameter of Wire for this purpose, allowing the wires themselves to be pure and confining the IO to the code that steps through the top-level wire(s). Is this incorrect?

like image 703
Doug McClean Avatar asked Apr 04 '14 00:04

Doug McClean


2 Answers

The simplest way to put data into a Wire s e m a b is via the input a. It's possible, through the use of WPure or WGen to get data out of the state delta s or the underlying Monad m, but these take us further away from the main abstractions. The main abstractions are Arrow and Category, which only know about a b, and not about s e m.

Here's an example of a very simple program, providing input as the input a. double is the outermost wire of the program. repl is a small read-eval-print loop that calls stepWire to run the wire.

import FRP.Netwire
import Control.Wire.Core

import Prelude hiding (id, (.))

double :: Arrow a => a [x] [x]
double = arr (\xs -> xs ++ xs)

repl :: Wire (Timed Int ()) e IO String String -> IO ()
repl w = do
    a <- getLine
    (eb, w') <- stepWire w (Timed 1 ()) (Right a)
    putStrLn . either (const "Inhibited") id $ eb
    repl w'

main = repl double

Notice that we pass in the time difference to stepWire, not the total elapsed time. We can check that this is the correct thing to do by running a different top-level wire.

timeString :: (HasTime t s, Show t, Monad m) => Wire s e m a String
timeString = arr show . time

main = repl timeString 

Which has the desired output:

a
1
b
2
c
3
like image 81
Cirdec Avatar answered Oct 15 '22 21:10

Cirdec


I just solved this in an Arrow way, so this might be more composible. You can read my posts if you like. Kleisli Arrow in Netwire 5? and Console interactivity in Netwire?. The second post has a complete interactive program

First, you need this to lift Kleisli functions (That is, anything a -> m b):

mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a

Then, assuming you want to get characters from terminal, you can lift hGetChar by doing this:

inputWire :: Wire s () IO () Char
inputWire = mkKleisli $ \_ -> hGetChar stdin

I haven't tested this runWire function (I just stripped code off from my previous posts), but it should run your wires:

runWire :: (Monad m) => Session m s -> Wire s e m () () -> m ()
runWire s w = do
  (ds, s') <- stepSession s
  -- | You don't really care about the () returned
  (_, w') <- stepWire w ds (Right ())
  runWire s' w'

You can compose the input wire wherever you like like any other Wires or Arrows. In my example, I did this (don't just copy, other parts of the program are different):

mainWire = proc _ -> do 
  c <- inputWire -< ()
  q <- quitWire -< c
  outputWire -< c
  returnA -< q

Or, one-liner:

mainWire = inputWire >>> (quitWire &&& outputWire) >>> arr (\(q,_) -> q)
like image 26
Carl Dong Avatar answered Oct 15 '22 20:10

Carl Dong