I'm converting some Python code to Haskell. The business logic is fairly complex and my Haskell code is getting ugly.
My Python function:
def f(some_state):
doAction1() # e.g. send_message("Hi there!")
if not userIsAuthenticated:
doAction2() # e.g. send_message("Please login")
return
if not userHasPermission:
doAction3() # e.g. send_message("Please sudo")
return
if somePredicate3:
doAction4()
if somePredicate4:
doAction5()
return
I have two constraints for the Haskell version:
- In order to keep my code as pure possible, I'd like to collect all my actions in a list (or a Free Monad if I'm feeling fancy) and then (outside the function) execute it. In Python, I would defined
tmp = []
at the beginning of my function, append totmp
all my actions (without calling them) and returntmp
. Then, I'd iterate over the list to execute all the actions (not Pythonic at all!). - Not end up with pushed-to-the-right craziness like this:
-
if a
then undefined
else if b
then undefined
else if c
then undefined
else if d
then undefined
else undefined
Basically, I want to collect a list of actions and exit early if necessary, so that I can write something like:
tell Action1
when somePredicate1 $ tell doAction2
when somePredicate2 $ tell doAction3
...
tell
is NOT from the writer monad but it's a function (which doesn't exist) that aggregates everything that has been "told" so far (à la writer monad) and is returned when I exit.
This really feels like a generalized Either monad, except that I need to keep all the actions that happened before the first Left (and not just the Left value) and then exit early at the first Left. I think the solution probably builds on MonadPlus or Alternative somehow, but I just can't figure out something nice. Basically, a fancy guard
.
It's also definitely possible that I'm having too much fun over-engineering this and I should just accept that my code has to drift right. But that wouldn't be elegant, would it?
Edit: For clarity, I want a function of type f :: State -> [Action]
, where data Action = Action1 | Action2 | Action3
.