如何替代case of精简代码?求优化嵌套case匹配的fmap代码方案
case of for Cleaner Haskell Code Great question! Nested case expressions can quickly make code feel clunky and hard to follow—especially when dealing with nested Maybe values like in your example. Let's break down both general alternatives to case of and a cleaner way to rewrite your specific code.
General Alternatives to case of for Maybe Values
When working with Maybe, you have several idiomatic alternatives to nested case statements:
- Monadic Do Notation: Since
Maybeis a monad, you can use do-notation to flatten nested checks into linear, readable code. Any step that returnsNothingwill short-circuit the entire block, just like your nestedcasestatements. - Maybe Combinators: Use functions like
>>=(bind),>>(sequence without using the result), and<$>(fmap) to chain operations onMaybevalues. This avoids explicit case matching entirely. - The
maybeHelper Function: This is a built-in function that replaces basiccasechecks forMaybe. It takes a default value, a function to run on theJustvalue, and theMaybeitself:maybe defaultValue f maybeValue. - Lens Operations: If you're already using lens (like your
^.operator), tools like^?(safe lens access, returnsMaybe),preview, andfilteredlet you concisely traverse nested optional values without case statements.
Cleaning Up Your Specific Code
Your original code is trying to extract the sSheetId only if both sProperties and sTitle exist (i.e., are Just values). Here are several cleaner implementations:
Version 1: Do Notation (Most Readable)
This flattens the nested cases into a linear flow that's easy to follow:
fmap (\eachSheet -> do sheetProperties <- eachSheet ^. sProperties _ <- sheetProperties ^. sTitle -- We don't need the title value, just its existence pure (sheetProperties ^. sSheetId)) listOfSheets
Version 2: Monadic Combinators (Concise)
Using >>= and >> to chain the checks without do-notation:
fmap ( (>>= \sp -> sp ^. sTitle >> pure (sp ^. sSheetId)) . (^. sProperties) ) listOfSheets
Version 3: maybe Function (Functional Style)
Replace each nested case with a maybe call for a more point-free feel:
fmap (maybe Nothing (\sp -> maybe Nothing (\_ -> sp ^. sSheetId) (sp ^. sTitle)) . (^. sProperties)) listOfSheets
Version 4: Lens-Specific Approach
Leverage lens's safe access (^?) and traversal tools to make it even more idiomatic:
fmap (\eachSheet -> do sp <- eachSheet ^? sProperties . _Just _ <- sp ^? sTitle . _Just pure (sp ^. sSheetId)) listOfSheets
Or an even more compact lens version using preview and filtered:
import Control.Lens (filtered, preview) fmap (preview (sProperties . _Just . filtered (\sp -> isJust (sp ^. sTitle)) . sSheetId)) listOfSheets
All these versions behave exactly like your original code but are much easier to read and maintain. Which one you choose depends on your preference for readability vs. conciseness, and how deeply you're already using lens in your codebase.
内容的提问来源于stack exchange,提问作者Samuel D'costa

