I gave a short talk at the local Haskell meetup yesterday about the library “Machines” by the ever-so-famous Edward Kmett.
This is a quick roundup of what I learned, and the resources I ran across.
- Github Repo of my example code.
- Hackage Page for Machines
- Github for Machines
- Slide deck by Rúnar Óli Bjarnason
- glguy’s 2048 clone
The initial task I gave myself to learn was to read an input line, and report how many words were in that line.
That consisted of 3 machines wired together in a pipeline. I only had to write a custom function for the worker in the middle. And even that was a one-liner.
auto function (and it’s
autoM friend) seem like the easiest way to create
a simple mapper type machine that takes some input, does a bit of work, and spits out
eachLineCount :: IO () eachLineCount = runT_ $ repeatedly (yield =<< liftIO getLine) ~> countWords ~> autoM print countWords :: Process String Int countWords = auto (length . splitOn " ")
Teeing two inputs together
The other big thing I tackled was the
Tee type. It lets you read from one of
two incoming streams of data, explicitly. For example, logically you can say:
“Give me the next value off the left stream”
There’s another type of multi-input machine I didn’t dive into called
allows for a blind
await in the consuming end, and the left pipe will be read
until its empty, and then the right pipe will be read (as opposed to explicitly
asking for Left or Right on a Tee)
Actually building the Tee was relatively simple once I figured out the
function. I have a commented out version at the bottom of the next snippet that
manually assembled the Tee using
capR. It is equivalent to
the much shorter
compareLineCounts :: String -> IO () compareLineCounts fixedString = runT_ $ tee (repeated fixedString ~> countWords) (ioInput ~> countWords) mergeInput ~> compareWords ~> autoM putStrLn ioInput :: (MonadIO m) => SourceT m String ioInput = repeatedly $ do liftIO $ putStrLn "Enter your new line to compare: " x <- liftIO getLine yield x mergeInput :: Tee a a (a,a) mergeInput = repeatedly $ do x <- awaits L y <- awaits R yield (x, y) compareWords :: (Ord a) => Process (a, a) String compareWords = repeatedly $ do (x,y) <- await yield $ case compare x y of GT -> "Greater Than" LT -> "Less Than" EQ -> "Equal To" -- compareLineCounts :: String -> IO () -- compareLineCounts fixedString = -- runT_ $ (capR (repeated fixedString ~> countWords) $ -- addL (ioInput ~> countWords) -- mergeInput) -- ~> compareWords -- ~> autoM putStrLn
Many thanks are in order to @kmett, @yoeight, @glguy, @cartazio and everybody else I asked questions of, all of whom helped me immensely on IRC.