• =?UTF-8?B?TWl4aW5nIG1vbmFkcyAvIGJpbmRpbmc=?=

    From =?UTF-8?B?VGhvYWkgTmd1eWVu?=@21:1/5 to All on Tue Aug 6 14:54:01 2019
    Hi fam,

    This is a fairly noob question. Considering the following imaginary functions:

    someCalculation :: String -> Maybe String
    someCalculation = Just

    main :: IO ()
    main = getLine >>= someCalculation >>= putStrLn


    What I want to do is to run a non-deterministic calculation on an input string, then print the output. In this case, 2 different monads are in use (Maybe & IO). Of course this wouldn't work due to incorrect typing.

    What would be the right way to handle something like this in Haskell?

    The someCalculation implementation is just a placeholder, but you get the idea...

    Many thanks,

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Benjamin Esham@21:1/5 to Thoai Nguyen on Tue Aug 6 19:37:54 2019
    Thoai Nguyen wrote:

    Hi fam,

    This is a fairly noob question. Considering the following imaginary functions:

    someCalculation :: String -> Maybe String
    someCalculation = Just

    main :: IO ()
    main = getLine >>= someCalculation >>= putStrLn

    What I want to do is to run a non-deterministic calculation on an input string, then print the output. In this case, 2 different monads are in use (Maybe & IO). Of course this wouldn't work due to incorrect typing.

    What would be the right way to handle something like this in Haskell?

    The someCalculation implementation is just a placeholder, but you get the idea...

    In Haskell, any non-deterministic calculation is going to end up producing
    an IO value, like IO String or IO (Maybe Int) or something like that. More formally, people refer to this as "a value of type 'IO a'," where "a" is
    what's called a "type variable." (In more complicated programs this often
    ends up being somewhat obscured by a bunch of monad transformers that rest
    on top of the IO monad, so the return value of an impure function might be "AppT String" instead of "IO String", but under the covers the IO monad is still involved.)

    If your someCalculation function is impure, it needs to have a type more
    like

    someCalculation :: String -> IO (Maybe String)

    if it's possible for the function to fail in some way, or

    someCalculation :: String -> IO String

    if not. It sounds to me as if your function can indeed fail, so I'll assume that it's going to return an IO (Maybe String). If you haven't seen this
    kind of notation before, it means "a String value that may or may not be
    there, embedded within the IO monad." I recommend reading this kind of thing from the inside out. Note that the type Maybe (IO String) is something completely different (and I'm not sure I could even describe what it is...
    I've never seen that kind of construction before, I don't think.)

    A dummy implementation of the someCalculation function with this type
    signature looks like

    someCalculation :: String -> IO (Maybe String)
    someCalculation = return . Just

    or, equivalently,

    someCalculation :: String -> IO (Maybe String)
    someCalculation s = do
    return (Just s)

    In either version, we first call "Just" on the input to convert it from a String to a Maybe String. Next we call "return" on the Maybe String to
    convert it to an IO (Maybe String).

    As for how you'd actually use it, I'd recommend (at least for now) using do-notation and pattern matching. Your main function as it exists now could
    be rewritten

    main = do
    input <- getLine
    result <- someCalculation input
    putStrLn result

    (although as you point out, this will be rejected by the compiler because "someCalculation input" has the type Maybe String, not the type IO String
    that you need in this context). With the new version of someCalculation,
    your main function would become

    main = do
    input <- getLine
    maybeResult <- someCalculation input
    case maybeResult of
    Just result -> putStrLn result
    Nothing -> putStrLn "Oops, it didn't work!"

    The case statement is what allows you to deal with the two possibilities for "maybeResult": it's either "Just result" or else it's "Nothing". You can do completely different things in the branches of the case statement, if you
    want, but both branches must have the same type, and in this particular situation that type must be IO (). This is also the return type of putStrLn,
    so we're good there.

    If you're in the kind of situation where, in the case of an error, you want
    to just swap in a default value and continue on, you could use the
    "fromMaybe" function:

    import Data.Maybe (fromMaybe)

    main = do
    input <- getLine
    maybeResult <- someCalculation input
    let result = fromMaybe "default value" maybeResult
    putStrLn result

    Hope this helps,

    Benjamin

    --
    Benjamin Esham
    https://esham.io

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Barry Fishman@21:1/5 to Thoai Nguyen on Wed Aug 7 09:30:06 2019
    On 2019-08-06 14:54:01 +00, Thoai Nguyen wrote:
    This is a fairly noob question. Considering the following imaginary functions:

    someCalculation :: String -> Maybe String
    someCalculation = Just

    main :: IO ()
    main = getLine >>= someCalculation >>= putStrLn


    What I want to do is to run a non-deterministic calculation on an
    input string, then print the output. In this case, 2 different monads
    are in use (Maybe & IO). Of course this wouldn't work due to incorrect typing.

    What would be the right way to handle something like this in Haskell?

    The someCalculation implementation is just a placeholder, but you get the idea...

    The first >>= needs to have the same type of Monad on each side of it,
    which you are not doing. Also if you want to output the result though
    putStrLn you need a function that converts you Maybe back to a String.

    answerString :: Maybe String -> String
    answerString = show

    If you want to keep someCalculation free of the IO Monad you can nest
    the Maybe inside the IO Monad:

    getLine >>= return . someCalculation >>= putStrLn . answerString

    or:

    getLine >>= return . answerString . someCalculation >>= putStrLn

    Note that any 'someCalculation >>= whatever' must return a Maybe, so you
    can't go from 'Maybe a' to 'IO a' using just >>= operations.
    --
    Barry Fishman

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?B?VGhvYWkgTmd1eWVu?=@21:1/5 to All on Wed Aug 7 14:41:05 2019
    Thanks for your help guys. I'm gonna go with Barry's implementation (convert and wrap Maybe into IO)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)