diff --git a/Main.hs b/Main.hs index b102bbf..c312682 100644 --- a/Main.hs +++ b/Main.hs @@ -11,13 +11,10 @@ import Data.Either (rights) import System.IO (hFlush, stdout) import Text.Read (readMaybe) --- dunai -import Control.Monad.Trans.MSF.Maybe (runMaybeT, MaybeT, exit) - -- rhine import FRP.Rhine -import FRP.Rhine.SyncSF.Except +import FRP.Rhine.SyncSF.Except hiding (keepFirst) import FRP.Rhine.Clock.Realtime.Millisecond import FRP.Rhine.Clock.Realtime.Stdin import FRP.Rhine.Clock.Select @@ -29,17 +26,70 @@ import FRP.Rhine.ResamplingBuffer.Collect import Util -main :: IO () -main = do - _ <- runMaybeT $ flow mainRhine - return () +data Tea = Tea + { teaSort :: String -- ^ The sort, brand, type of tea + , duration :: Double -- ^ The duration to brew in minutes + } + deriving (Read, Show) + + +type TeaSimClock = Millisecond 100 +type TeaStatusClock = Millisecond 20000 + +type CommandClock = SelectClock StdinClock Tea +commandClock :: CommandClock +commandClock = SelectClock + { mainClock = StdinClock + , select = readMaybe + } + +userTeas :: SyncSF IO CommandClock () Tea +userTeas = proc _ -> do + tea <- timeInfoOf tag -< () + _ <- arrMSync putStrLn -< "Your request: " ++ show tea + returnA -< tea -mainRhine :: Rhine (MaybeT IO) (LiftClock IO MaybeT (Millisecond 50)) () () -mainRhine = timeless (listToMaybeS "Congratulations! You've installed the tutorial correctly!\n") - >-> liftS (putChar >>> (>> hFlush stdout)) - @@ liftClock waitClock +-- TODO Also record intermediate step where we just output a string instead of throwing an exception +countdownTea + :: Monad m + => Tea -> SyncSF (ExceptT String m) TeaSimClock a () +countdownTea Tea {..} = proc _ -> do + now <- timeInfoOf absolute -< () + start <- keepFirst -< now + let + minutesPassed = (now `diffTime` start) / 60 + done = (minutesPassed >= duration, teaSort) + _ <- throwOn' -< done + returnA -< () --- TODO In dunai 0.1.2 -listToMaybeS :: Monad m => [b] -> MSF (MaybeT m) a b -listToMaybeS = foldr iPost exit +exampleTea = Tea + { teaSort = "English Breakfast Tea" + , duration = 0.2 + } + +oneTea :: Tea -> SyncExcept IO TeaSimClock a () () +oneTea nextTea = do + -- nextTea <- currentInput + once_ $ putStrLn $ "Now brewing: " ++ show nextTea + teaSort <- try $ countdownTea nextTea + once_ $ putStrLn $ "Your " ++ teaSort ++ " is ready!" + step $ const $ return ((), ()) + +teas :: SyncSF IO TeaSimClock [Tea] [Either () ()] +teas = pool $ exceptS . runMSFExcept . oneTea + +teaStatus :: SyncSF IO TeaStatusClock [Either () ()] () +teaStatus = proc as -> do + let numberOfTeas = length $ rights as + arrMSync putStrLn -< show numberOfTeas ++ " teas currently brewing" + +mainRhine + = userTeas @@ commandClock + >-- collect -@- concurrently + --> teas @@ waitClock + >-- keepLast [] -@- concurrently + --> teaStatus @@ waitClock + +main :: IO () +main = flow mainRhine diff --git a/Presentation.tex b/Presentation.tex index 6bacc4a..d4f5058 100644 --- a/Presentation.tex +++ b/Presentation.tex @@ -91,6 +91,203 @@ \subsection{Synchronous arrowized FRP} \end{block} \end{frame} +\begin{frame}[fragile] + % TODO Copy from abstract + \begin{description}[<+->] + \item[Clock type] Describes when, and how often, + the clock should tick. + \item[Clock value] Implementation details + \begin{itemize}[<+->] + \item E.g. physical device address + \item Implementation choice + \end{itemize} + \item[Running clock] Side-effectful stream of \emph{time stamps}, tagged with additional info about the \emph{tick}. + \end{description} +\end{frame} + + +\begin{frame}[fragile] +\begin{block}<+->{\texttt{Rhine}} +\begin{minted}{haskell} +-- simplified here +class Clock m cl where + type TimeDomainOf cl -- time stamp + type Tag cl -- additional information about tick + startClock :: cl -> MSF m () (TimeInfo cl, Tag cl) +\end{minted} +\end{block} + +\begin{block}<+->{} +\begin{minted}{haskell} +data TimeInfo cl = {...} + -- absolute and relative time, tag +\end{minted} +\end{block} +\note<.>{While a clock only needs to output a time stamp, +\texttt{Rhine} calculates absolute and relative (to the start of the program and to the last tick) time.} +\end{frame} + + +\begin{frame}[fragile] +\visible<+->{A clock produces side effects to...} +\begin{itemize} + \item[...] wait between ticks, + \item[...] measure the current time, + \item[...] produce additional data (e.g. events). +\end{itemize} + +\begin{block}<+->{Examples of clocks} +\begin{itemize} + \item Fixed sample rate (e.g. \mintinline{haskell}{Millisecond n}) + \item Events (e.g. \mintinline{haskell}{Stdin}) +\end{itemize} +\end{block} +\end{frame} + + +\begin{frame}[fragile] +\begin{block}<+->{} +\begin{minted}{haskell} +type SyncSF m cl a b = MSF (ReaderT (TimeInfo cl)) a b +\end{minted} +\end{block} +\begin{block}<+->{Lifting \texttt{dunai} concepts} +\begin{minted}{haskell} +arrMSync :: (a -> m b) -> SyncSF m cl a b +\end{minted} +\end{block} +\begin{block}<+->{Time information} +\begin{minted}{haskell} +timeInfo :: SyncSF m cl () (TimeInfo cl) +\end{minted} +\end{block} +\begin{block}<+->{Basic signal processing} +\begin{minted}{haskell} +integral :: VectorSpace v => SyncSF m cl v v +\end{minted} +\end{block} +\end{frame} + +\subsection{Exceptions and control flow} + +\begin{frame}[fragile] +\begin{block}<+->{\mintinline{haskell}{ExceptT}...} +\begin{minted}{haskell} +--base, transformers +data Either e a = Left e | Right a +newtype ExceptT e m a = ExceptT (m (Either e a)) +\end{minted} +\end{block} + +\begin{block}<+->{...control flow! (Thanks to Paolo Capriotti)} +\begin{minted}{haskell} +-- dunai, rhine (simplified) +newtype SyncExcept m cl a b e + = SyncExcept (SyncSF (ExceptT e m) cl a b) +\end{minted} +\end{block} + +\begin{block}<+->{} +\begin{minted}{haskell} +instance Monad m => Monad (SyncExcept m cl a b) + +throwOn' :: SyncSF (ExceptT e m) cl (Bool, e) () +try :: SyncSF (ExceptT e m) cl a b + -> SyncExcept m cl a b e +safely :: SyncExcept m cl a b Empty -> SyncSF m cl a b +safe :: SyncSF m cl a b -> SyncExcept m cl a b e +\end{minted} +\end{block} +\end{frame} + +\begin{frame}[fragile] +\begin{block}<+->{Hello World!} +\begin{minted}{haskell} +type SumClock = Millisecond 100 + +fillUp :: SyncSF (ExceptT Double m) SumClock Double () +fillUp = proc x -> do + s <- integral -< x + _ <- throwOn' -< (s > 5, s) + returnA -< () + +helloWorld :: SyncExcept IO SumClock () () Empty +helloWorld = do + try $ arr (const 1) >>> fillUp + once_ $ putStrLn "Hello World!" + helloWorld + +main = flow $ safely helloWorld @@ waitClock +\end{minted} +\end{block} +\end{frame} + +\begin{frame}[fragile] +\begin{block}{Clock safety} +\begin{minted}{haskell} +fastSignal :: SyncSF m FastClock () a + +slowProcessor :: SyncSF m SlowClock a b + +clockTypeError = fastSignal >>> slowProcessor +\end{minted} +\end{block} +\begin{verbatim} +PresentationExamples.hs:67:33: error: + • Couldn't match type ‘SlowClock’ with ‘FastClock’ +\end{verbatim} +\end{frame} + +\section{Quick introduction to Rhine} + +\subsection{Synchronous arrowized FRP} + +\begin{frame}[fragile] +\begin{block}<+->{\texttt{Dunai} (Iván Pérez, Henrik Nilsson, MB)} +\begin{minted}{haskell} +data MSF m a b = MSF (a -> m (b, MSF m a b)) +\end{minted} +\end{block} +\begin{block}<+->{} +\begin{minted}{haskell} +-- Control.Arrow +(>>>) :: MSF m a b -> MSF m b c -> MSF m a c +(***) :: MSF m a b -> MSF m c d -> MSF m (a,c) (b,d) +arr :: (a -> b) -> MSF m a b +\end{minted} +\end{block} +\begin{block}<+->{} +\begin{minted}{haskell} +-- only dunai +arrM :: (a -> m b) -> MSF m a b +\end{minted} +\end{block} +\begin{itemize}[<+->] + \item \mintinline{haskell}{MSF (Reader Double)} is a replacement for \texttt{Yampa}. + \item Other monads allow for concise FRP paradigms: + \begin{itemize}[<+->] + \item \mintinline{haskell}{State}, \mintinline{haskell}{Reader} and \mintinline{haskell}{Writer} give global state variables. + \item \mintinline{haskell}{List} gives branching computations. + \item \mintinline{haskell}{Either} gives control flow! + \end{itemize} + \item Support for (entering and leaving) monad transformers. +\end{itemize} +\end{frame} + +\begin{frame}[fragile] +\begin{block}<+->{Arrow syntax} +\begin{minted}{haskell} +{-# LANGUAGE Arrows #-} + +verboseSum :: MSF IO Int Int +verboseSum = proc n -> do + s <- sumS -< n + _ <- arrM print -< "The sum is now " ++ show s + returnA -< s +\end{minted} +\end{block} +\end{frame} + \subsection{Clocks} \begin{frame}[fragile] diff --git a/rhine-tutorial.cabal b/rhine-tutorial.cabal index ea9b8d1..becdab4 100644 --- a/rhine-tutorial.cabal +++ b/rhine-tutorial.cabal @@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: rhine-tutorial -version: 0.1.2.0 +version: 0.3.0.0 -- synopsis: -- description: license: BSD3