{- | Description: IO monad without do notation Copyright: Copyright (C) 2023 Yoo Chung License: GPL-3.0-or-later Maintainer: dev@chungyc.org Part of Ninety-Nine Haskell "Problems". Some solutions are in "Solutions.P74". -} module Problems.P74 ( askGoldbach -- * Original function -- | The function below is the equivalent implementation of 'askGoldbach' using do notation. , askGoldbach' -- * Supporting function -- | The function below is not part of the problem. -- This is for use in examples and tests. , withFixedInput) where import Solutions.P40 (goldbach) import qualified Solutions.P74 as Solution import System.IO import System.Process (createPipe) -- $setup -- >>> import System.IO {- | We would like to implement a function which reads an even number from standard input, finds two prime numbers which add up to the number (see 'Problems.P40.goldbach'), and prints out the equation to standard output. Given the use of input and ouput, this could be written with the IO monad in do notation: @ askGoldbach :: Handle -> Handle -> IO () askGoldbach hIn hOut = do s <- hGetLine hIn let n = read s :: Int let (a,b) = goldbach n hPutStr hOut $ show n hPutStr hOut "=" hPutStr hOut $ show a hPutStr hOut "+" hPrint hOut b @ Implement the function without do notation. In other words, use '>>=' or '>>' directly, instead of using them implicitly through do notation. Try to use these functions with prefix style instead of infix style. === Examples >>> withFixedInput "104" stdout askGoldbach 104=3+101 === __Hint__ In do notation, > do > a > b is a shorthand for > (>>) a b or equivalently, > (>>=) a (\_ -> b) Also, > do > x <- a > b is a shorthand for > (>>=) a (\x -> b) For those not familiar with monads, using these functions directly in prefix style will make it more apparent that these are not just sequenced statements as in imperative languages, but really a series of function applications. The distinction may be minor in the context of IO monads, but it will make it easier to understand other kinds of monads such as 'Maybe'. While not relevant to this problem, also note that 'return' is /not/ a return statement as in most languages, but a function that injects a value into the monad. For example, > f :: SomeMonad (Int,Int) > f = do > (a,b) <- return (1,2) > return (a+1,b+1) does not return @(1,2)@, but instead returns a monad with @(2,3)@. The naming of 'return' can make the function body similar to what one would expect from imperative languages. For example, > f :: IO String > f = do > s <- getLine > return $ "Got string: " ++ s But one should be careful to remember that it is not a return statement as such. -} askGoldbach :: Handle -> Handle -> IO () askGoldbach :: Handle -> Handle -> IO () askGoldbach = Handle -> Handle -> IO () Solution.askGoldbach {- | Reads an even number from standard input, finds two prime numbers which add up to the number, and prints out the equation to standard output. This is an implementation of 'askGoldbach' in do notation. === Examples >>> withFixedInput "104" stdout askGoldbach' 104=3+101 -} askGoldbach' :: Handle -> Handle -> IO () askGoldbach' :: Handle -> Handle -> IO () askGoldbach' Handle hIn Handle hOut = do String s <- Handle -> IO String hGetLine Handle hIn let n :: Int n = String -> Int forall a. Read a => String -> a read String s :: Int let (Int a,Int b) = Int -> (Int, Int) forall a. Integral a => a -> (a, a) goldbach Int n Handle -> String -> IO () hPutStr Handle hOut (String -> IO ()) -> String -> IO () forall a b. (a -> b) -> a -> b $ Int -> String forall a. Show a => a -> String show Int n Handle -> String -> IO () hPutStr Handle hOut String "=" Handle -> String -> IO () hPutStr Handle hOut (String -> IO ()) -> String -> IO () forall a b. (a -> b) -> a -> b $ Int -> String forall a. Show a => a -> String show Int a Handle -> String -> IO () hPutStr Handle hOut String "+" Handle -> Int -> IO () forall a. Show a => Handle -> a -> IO () hPrint Handle hOut Int b {- | Given a string and an output handle, apply the function to a constructed input handle and the given output handle. The input handle will read the given string. === Examples >>> withFixedInput "This is the input." stdout (\i -> \o -> hGetLine i >>= hPutStrLn o) This is the input. -} withFixedInput :: String -> Handle -> (Handle -> Handle -> IO ()) -> IO () withFixedInput :: String -> Handle -> (Handle -> Handle -> IO ()) -> IO () withFixedInput String s Handle hOut Handle -> Handle -> IO () f = do (Handle readEnd, Handle writeEnd) <- IO (Handle, Handle) createPipe Handle -> String -> IO () hPutStrLn Handle writeEnd String s Handle -> IO () hClose Handle writeEnd Handle -> Handle -> IO () f Handle readEnd Handle hOut Handle -> IO () hClose Handle readEnd