3

I am playing around with the machines module by Edward Kmett, and I'm getting a little confused here and there. I thought the best way to ask a question is to provide a toy use case. Described below.

Machines one and two sit at two prongs of a Wye.

Machine one takes as param a list of ints, and pushes it down stream. Machine two takes as param a list of chars, and pushes it down stream.

Machine three keeps an internal state, beginning at mempty, then mappends the elements it receives from either machines from the Wye base on some condition (so not both). Machine three then gets the current state and pushes it downtream.

Machine four prints the elements it receives to console.

So far I've got this:

y1 :: PlanT k [Int] m ()
y1 = yield

y2 :: PlanT k [Char] m ()
y2 = yield

But I'm not sure how to combine y1 and y2; or roll an arbitrary process that hides a state, as opposed to using one of the stock combinators exported by Process.hs.

Per suggestion, the link to the machines package: http://hackage.haskell.org/package/machines

And a pdf giving a very high level description of what it does: https://dl.dropboxusercontent.com/u/4588997/Machines.pdf

Gilles 'SO- stop being evil'
  • 92,660
  • 35
  • 189
  • 229
xiaolingxiao
  • 4,340
  • 3
  • 27
  • 64
  • Why was this question downvoted? – bennofs Jun 26 '13 at 17:24
  • @bennofs Can't speak for the down-voter, but I don't see a question here. – jerry Jun 26 '13 at 17:28
  • My question is how to implement this, since I just started using machines, I can't provide a block of code to debug. – xiaolingxiao Jun 26 '13 at 17:34
  • 3
    @jerry @closevoters `machines` is __NEW__. Edward Kmett is worth watching and it's worth the effort to get your head round new stuff he does. If this is obvious to you, answer. If this is unclear to you, it's probably because us of `machines` is unclear to a lot of people right now. If you understand `pipes` and `conduit`, then it's OK to say this isn't well worded, but if you just don't understand it at all, that's possibly because it's bleeding edge and you ought to go understand pipes first before you decide whether this can be better put. – AndrewC Jun 26 '13 at 18:27
  • I think it's confusing due to the use of the word 'Wye'. What you're attempting to do is merge two lists of different types. I don't know enough of haskell to know if that's possible, but my gut says no. Also, it would be useful to link to the machines module being used. If it's bleeding edge, you can't just assume people have a general knowledge of it. – Shaz Jun 26 '13 at 18:50
  • 2
    @AndrewC The question has been editted several times since [the version I was commenting on](http://stackoverflow.com/revisions/17326560/1). That version literally did not contain a question (or a statement of what chibro2 was trying to do). – jerry Jun 26 '13 at 18:52
  • @AndrewC Thanks for the support! It feels awesome. And Jerry thank you for noting I did not ask a question, the original post was poorly phrased – xiaolingxiao Jun 26 '13 at 19:05
  • @RyanWH I'm not familliar with `machines`, but the usual way to have a single list of `Ints` and `Chars` is to define `data IntOrChar = I Int | C Char` and then have a list of `IntOrChar`s. – Neil Forrester Jun 26 '13 at 19:06
  • 1
    @RyanWH as I understand Wye is 'Machine' that can read from two input stream in a non-deterministic manner, so machine three can either read from list of chars, or list of ints, and build a new list from it. But the new list only contain ints OR chars, not both. The point of the question is really in how to combine different machines to make a bigger one, what is going on here with the list stuff is incidental. Sorry if that was poorly phrased too. – xiaolingxiao Jun 26 '13 at 19:07
  • @jerry It was in the title of the question "Could someone provide a machines implementation of the following plan?" In Kmett's machines, the words `machine` and `plan` have a specific meaning. Perhaps that's why the question felt confusing - if you took the words machine and plan with their everyday meaning. – AndrewC Jun 26 '13 at 20:31
  • 1
    @AndrewC You're correct that I didn't understand the special meanings of `machine` or `plan`, but I still stand by my comment that I (originally) didn't see a question. Most users do not consider "code this for me" a valid question on Stack Overflow (though, personally, sometimes I'll answer anyway if I find the topic sufficiently interesting). To chibro2's credit, he or she has improved the question to the point that I feel it's a fit for the site (though I'm not nearly proficient enough at Haskell or the `machines` concept to have a go at it). – jerry Jun 27 '13 at 14:20

1 Answers1

5

I'm also a beginer with machines, here is my result:

import Control.Monad
import Data.Char (intToDigit)
import Data.Machine
import Data.Machine.Plan
import Data.Machine.Source

-- | Produces integers from a list.
m1 :: [Int] -> Source Int
m1 = source

-- | Produces characters from a list.
m2 :: [Char] -> Source Char
m2 = source

-- | Reads a number from its left input. Then reads this many
-- characters from its right input. Outputs the resulting string,
-- together with the number of strings produced so far.
m3 :: Tee Int Char (Int, String)
m3 = construct (loop 0)
  where
    -- `loop` keeps internal state - the count of strings
    -- produced so far.
    loop count = do
        -- Read a number from L.
        n <- awaits L
        -- Read this many characters from L.
        s <- replicateM n (awaits R)
        let count' = count + 1
        -- Output the result.
        yield (count', s)
        loop count'

main = print . run $ tee (m1 [2,3,4,5])
                         (m2 "Lorem ipsum dolor sit amet") m3

I haven't used a monoid in m3, I used plain numbers instead, but the idea is the same. I also used Tee instead of Wye, because my example needs deterministic input - it chooses if it reads from L or R. But using Wye for a similar purpose would be just the same.

Update: Surely it's possible to use State instead of Identity to keep track of the count. For example:

m3State :: TeeT (State Int) Int Char (Int, String)
m3State = repeatedly $ do
        n <- awaits L
        s <- replicateM n (awaits R)
        lift (modify (+ 1)) -- increment the counter
        count <- lift get   -- get the counter to output it
        yield (count, s)

main = print . flip evalState 0 . runT $ input m3State

I suspect that using repeatedly on a plan is slightly faster than having an explicit monadic loop, but I think in this small example the difference is negligible.

Or if we wanted to just count the number of strings and output it only at the end, we could use Writer (Sum Int) instead. Full code here.

Petr
  • 60,177
  • 8
  • 136
  • 295
  • This is amazing! Almost exactly what I was looking for. Is there any way Tee can wrap a State monad? This way the count can be incred by calling `modify (+1)` and instead of explicitly calling `loop count'`, we can just write loop count = forever $ do {...} ? – xiaolingxiao Jun 27 '13 at 01:27
  • 1
    @chibro2 Yes, I updated the example. I noticed that _machines_ have a special combinator `repeatedly`, which I suspect to be slightly faster. – Petr Jun 27 '13 at 05:50