transient-haskell / transient

A full stack, reactive architecture for general purpose programming. Algebraic and monadically composable primitives for concurrency, parallelism, event handling, transactions, multithreading, Web, and distributed computing with complete de-inversion of control (No callbacks, no blocking, pure state)
MIT License
630 stars 28 forks source link

Issues with the logging API #55

Open harendra-kumar opened 7 years ago

harendra-kumar commented 7 years ago

With the current design of the logging primitives, it is possible to easily run into inconsistent behavior. For example:

main = keep $ restore $ do
     r <- choose [1..5 :: Int]
     logged $ liftIO $ print ("A",r)
     suspend ()
     liftIO $ print ("B",r)

Here, the programmer forgot to add logged to the choose primitive. This results in an incorrect output. Another example, if we use suspend without logging, the program always suspends:

main = keep $ do
     r <- choose [1..5 :: Int]
     liftIO $ print ("A",r)
     suspend ()
     liftIO $ print ("B",r)

Instead we should either throw an error or ignore the suspend since we are not logging.

A potential solution to these problems is to design the API such that we use the logged primitive one level above the suspend. If the parent is logged then logging is inherited by all the child computations and we can suspend a child. Since we know the logged/not logged state we can change the behavior of suspend based on that. Also, the logged primitive itself can do the job of restore as well. The code will look like:

main = keep $ logged $ do
     r <- choose [1..5 :: Int]
     liftIO $ print ("A",r)
     suspend ()
     liftIO $ print ("B",r)

I have not thought much about it, so I am not sure if this solution has any inherent problems.

agocorona commented 7 years ago

Hi, Harendra I had this problen, that's is why I created the Cloud monad, to force logging.. The solution is to promote logged to the Cloud Monad, and rename the cloud monad to something more general that includes logging/recovery for any purpose.

2017-05-13 22:00 GMT+02:00 Harendra Kumar notifications@github.com:

With the current design of the logging primitives, it is possible to easily run into inconsistent behavior. For example:

main = keep $ restore $ do r <- choose [1..5 :: Int] logged $ liftIO $ print ("A",r) suspend () liftIO $ print ("B",r)

Here, the programmer forgot to add logged to the choose primitive. This results in an incorrect output. Another example, if we use suspend without logging, the program always suspends:

main = keep $ do r <- choose [1..5 :: Int] liftIO $ print ("A",r) suspend () liftIO $ print ("B",r)

Instead we should either throw an error or ignore the suspend since we are not logging.

A potential solution to these problems is to design the API such that we use the logged primitive one level above the suspend. If the parent is logged then logging is inherited by all the child computations and we can suspend a child. Since we know the logged/not logged state we can change the behavior of suspend based on that. Also, the logged primitive itself can do the job of restore as well. The code will look like:

main = keep $ logged $ do r <- choose [1..5 :: Int] liftIO $ print ("A",r) suspend () liftIO $ print ("B",r)

I have not thought much about it, so I am not sure if this solution has any inherent problems.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/transient-haskell/transient/issues/55, or mute the thread https://github.com/notifications/unsubscribe-auth/AAf5ivoZjxm72on5mj3iUaPYJMMHjuh-ks5r5gvYgaJpZM4NaLVi .

-- Alberto.

harendra-kumar commented 7 years ago

Yeah, I figured that when I later looked at the Cloud monad.