Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO Monad #265

Open
lawrence-koh opened this issue May 26, 2022 · 5 comments
Open

IO Monad #265

lawrence-koh opened this issue May 26, 2022 · 5 comments

Comments

@lawrence-koh
Copy link

Hi @Dobiasd,

I worked through your Udemy functional programming course and found functional programming and fplus to be highly interesting. Am currently attempting to refactor some code and am just wondering if there is a way to model IO monads using fplus?

I have a function that is currently as such:

//func : [String] -> Result String String
fplus::result< std::string, std::string > func( const std::vector< std::string >& param )

However, I would like to add some logging at the start of the function. This would change the return type since logging is an IO action which may fail, but not too sure how it can be done using fplus.

Be great if I could get your advice :)

@Dobiasd
Copy link
Owner

Dobiasd commented May 30, 2022

Hi, and thanks for the interesting question! 🙂

I don't have a solution at hand, but I'll think about it and get back to you.

@Dobiasd
Copy link
Owner

Dobiasd commented Jun 4, 2022

Could you explain in a bit more detail what you'd like to archive (or maybe show a minimal example in Haskell)?

Do you want func to not be called at all if the logging, which is done before it, fails, and get a logging-failed result? Or should func be called nonetheless, and the logging-failed information shall be transported somewhere in addition to the result<string, string> from func?

@lawrence-koh
Copy link
Author

I was thinking of something like the below:

//func : [String] -> Result String String
fplus::result< std::string, std::string > func( const std::vector< std::string >& param )
{
   log("Performing func..."); -> this will throw if the logging fails
  //actual code for func
}

If the log function fails and throws, I would like the logging failed information to be transported in addition to the result<string, string>. I suppose one way would be just to change the return type of result to something else?

@Dobiasd
Copy link
Owner

Dobiasd commented Jun 6, 2022

I would like the logging failed information to be transported in addition to the result<string, string>.

Ok, so if I understand correctly, no matter if the logging fails or not, you want your actual function to be executed nonetheless. Because otherwise, it would just be simple monadic composition, which could be achieved using things like fplus::and_then_result (((a -> Result c b), (Result a b)) -> Result c b) or fplus::compose_result (((a -> Result b c), (b -> Result d c)) -> (a -> Result d c)).

But in your case, we can have the following possible results:

  • A) logging succeeded and func succeeded
  • B) logging succeeded and func failed
  • C) logging failed and func succeeded (would not exist in monadic composition)
  • D) logging failed and func failed (would not exist in monadic composition)

Thus, just thinking about the types for now, we need a return type for your final (composed) function able to represent all those states.

For now, we have:

  • log : a -> Result Monostate String
    • a would probably be [String] in your case, but trying to simplify be generalizing here.
    • Monostate is just a unit type, like std::monostate, which would be returned if the logging succeeded
    • String would be the error message explaining why the log failed, for example, the what() part of the exception that would be thrown.
  • func : a -> Result String String
    • This is your normal function, and we likely don't need to care what the two String cases mean.

So the result of the new log_and_func function could contain (per case):

  • A) a String (from func success)
  • B) a String (from func fail)
  • C) a (String, String) (from log fail and from func success)
  • D) a (String, String) (from log fail and from func fail)

Thus we could have log_and_func : a -> (Maybe String, Result String String). Example result pairs:

  • A) (Nothing, "func_result")
  • B) (Nothing, "func_error")
  • C) ("log_error", "func_result")
  • C) ("log_error", "func_error")

Is the above going in the right direction, or do you intend something else?

@lawrence-koh
Copy link
Author

yes what you outlined makes sense!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants