|
| 1 | +module Bluefin.Examples.HandleReader where |
| 2 | + |
| 3 | +import Bluefin.Compound (Handle (mapHandle), useImpl) |
| 4 | +import Bluefin.Eff (Eff, runEff, (:&), (:>)) |
| 5 | +import Bluefin.HandleReader |
| 6 | + ( HandleReader, |
| 7 | + askHandle, |
| 8 | + localHandle, |
| 9 | + runHandleReader, |
| 10 | + ) |
| 11 | +import Bluefin.IO (effIO) |
| 12 | +import Bluefin.State (State, evalState, get, modify) |
| 13 | +import Bluefin.Stream (Stream, forEach, yield) |
| 14 | + |
| 15 | +-- | @SummableStream@ is a @Stream@ that can be locally overridden |
| 16 | +newtype SummableStream e = MkSummableStream (HandleReader (Stream Int) e) |
| 17 | + |
| 18 | +-- | Run the @SummableStream@ in a @Stream@ |
| 19 | +runSummableStream :: |
| 20 | + (e1 :> es) => |
| 21 | + Stream Int e1 -> |
| 22 | + (forall e. SummableStream e -> Eff (e :& es) r) -> |
| 23 | + Eff es r |
| 24 | +runSummableStream y k = |
| 25 | + runHandleReader y $ \hr -> do |
| 26 | + k (MkSummableStream hr) |
| 27 | + |
| 28 | +-- | Yield to the @SummableStream@ |
| 29 | +yieldSummable :: |
| 30 | + (e1 :> es) => |
| 31 | + SummableStream e1 -> |
| 32 | + Int -> |
| 33 | + Eff es () |
| 34 | +yieldSummable (MkSummableStream hr) n = onHandle hr (\y -> yield y n) |
| 35 | + |
| 36 | +onHandle :: |
| 37 | + (Handle h, e1 :> es) => |
| 38 | + HandleReader h e1 -> |
| 39 | + (forall e. h e -> Eff e r) -> |
| 40 | + Eff es r |
| 41 | +onHandle hr k = do |
| 42 | + h <- askHandle hr |
| 43 | + k h |
| 44 | + |
| 45 | +-- | Locally override the @SummableStream@ so that @yieldSummable@, as |
| 46 | +-- well as yielding to the @Stream@ as normal, also accumulates into |
| 47 | +-- the @State@. |
| 48 | +sumYields :: |
| 49 | + (e1 :> es, e2 :> es) => |
| 50 | + SummableStream e1 -> |
| 51 | + State Int e2 -> |
| 52 | + Eff es r -> |
| 53 | + Eff es r |
| 54 | +sumYields (MkSummableStream hr) st body = do |
| 55 | + yorig <- askHandle hr |
| 56 | + -- In the body, hr is modified so that it both modifies the State |
| 57 | + -- and yields to the original Stream |
| 58 | + forEach |
| 59 | + ( \ynested -> do |
| 60 | + localHandle |
| 61 | + hr |
| 62 | + ( \_ -> mapHandle ynested |
| 63 | + ) |
| 64 | + (useImpl body) |
| 65 | + ) |
| 66 | + ( \i -> do |
| 67 | + -- yield to the original Stream |
| 68 | + yield yorig i |
| 69 | + -- modify the State |
| 70 | + modify st (+ i) |
| 71 | + ) |
| 72 | + |
| 73 | +-- ghci> exampleHandleReader |
| 74 | +-- 1 |
| 75 | +-- 2 |
| 76 | +-- 3 |
| 77 | +-- 4 |
| 78 | +-- Total from summed block was 7 |
| 79 | +exampleHandleReader :: IO () |
| 80 | +exampleHandleReader = runEff $ \io -> do |
| 81 | + evalState 0 $ \st -> do |
| 82 | + forEach |
| 83 | + ( \y -> runSummableStream y $ \sb -> do |
| 84 | + yieldSummable sb 1 |
| 85 | + yieldSummable sb 2 |
| 86 | + sumYields sb st $ do |
| 87 | + -- The yields in this body will be accumulated into st |
| 88 | + yieldSummable sb 3 |
| 89 | + yieldSummable sb 4 |
| 90 | + ) |
| 91 | + (effIO io . print @Int) |
| 92 | + |
| 93 | + total <- get st |
| 94 | + let msg = "Total from summed block was " <> show total |
| 95 | + effIO io (putStrLn msg) |
0 commit comments