Changing a do expression that uses pattern matching to application of the bind operator

我与影子孤独终老i 提交于 2021-01-28 05:02:39

问题


Original question

LYAH, in For a Few Monads More shows this function,

solveRPN :: String -> Maybe Double  
solveRPN st = do  
    [result] <- foldM foldingFunction [] (words st)  
    return result

which uses pattern-matching in conjunction with do expression to esure that the monad coming out of foldM wraps a singleton list.

In order to truly understand the nature of the do expression as well as of Monad, I have been rewriting most example from that book using >>= and >> instead of the do expression, a practice which is suggested somewhere in Real World Haskell too, but I don't remember which chapter.

As regards the function above, I'm a bit puzzled. What is the most concise way to write it without using the do expression? The best I could come up with is

solveRPN :: String -> Maybe Double
solveRPN s = foldM step [] (words s) >>= \x -> case x of
                                               [y] -> Just y
                                               _   -> Nothing

but I was hoping in something cleaner, as this one is pretty noisy for 2 reasons:

  • it uses a lambda
  • it uses the case expression, which doesn't look much nicer than a if-then-else.

This question is related to the present one.

I asked another question which actually highlights the basic issue in this one:

How do I pull the head out of a list and succeed only for singleton lists?

And this has nothing to do with the Maybe that wraps the result in solveRPN.

Update

The answer I accepted there proposes a clear solution the question above:

func :: [a] -> a
func = foldr1 (const (const undefined))

which can be used to write easily solveRPN in pointfree style:

solveRPN :: String -> Maybe Double
solveRPN st = foldM foldingFunction [] (words st) >>= Just . foldr1 (const (const undefined))

However, this absolutely unsatisfying, as it does not fully take advantage of the Maybe monad, failing at runtime instead of returning Nothing when the output is incorrect.

I think that playing around with that could lead me or someone else to answer my original question without do/case/helpers.


回答1:


The desugaring of do notation specified in the Haskell Report actually includes a pattern match in order to handle pattern match failure with fail, now specified in the MonadFail typeclass. It may be written as a case or as a function.

do {e}                 =  e
do {e;stmts}           =  e >> do {stmts}
do {p <- e; stmts}     =  let ok p = do {stmts}
                              ok _ = fail "..."
                            in e >>= ok
do {let decls; stmts}  =  let decls in do {stmts}

So in your example, that could look like this.

solveRPN :: String -> Maybe Double  
solveRPN st = foldM foldingFunction [] (words st) >>= \ x -> case x of
    [result] -> return result
    _ -> fail "Pattern match failure in do expression at file:line:col"

(And of course fail message :: Maybe a is just Nothing.)

You can make this slightly more concise with LambdaCase to avoid the extra variable.

{-# LANGUAGE LambdaCase #-}

solveRPN :: String -> Maybe Double  
solveRPN st = foldM foldingFunction [] (words st) >>= \ case
    [result] -> return result
    _ -> fail "Pattern match failure in do expression at file:line:col"

That’s the standard desugaring, but if you want to golf this further, you can use a helper function. The standard listToMaybe :: [a] -> Maybe a works if you want to allow non-singleton lists.

import Data.Maybe (listToMaybe)

solveRPN :: String -> Maybe Double  
solveRPN st = foldM foldingFunction [] (words st) >>= listToMaybe

There is no standard singletonToMaybe but you can easily write one.

singletonToMaybe :: [a] -> Maybe a
singletonToMaybe [x] = Just x
singletonToMaybe _   = Nothing

-- or:

singletonToMaybe xs = guard (isSingleton xs) *> listToMaybe xs

isSingleton = null . drop 1

Then write the function in point-free style using the monadic composition operators <=< or >=>.

solveRPN :: String -> Maybe Double  
solveRPN = singletonToMaybe <=< foldM foldingFunction [] . words


来源:https://stackoverflow.com/questions/62251843/changing-a-do-expression-that-uses-pattern-matching-to-application-of-the-bind-o

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!