Case comprehensions

From HaskellWiki

One could argue that the notation <- is misleading, suggesting the idea of drawn from as in a list comprehension. But it is very nice to reuse precisely the list comprehension syntax.

Pattern guards and Transformational Patterns, Martin Erwig and Simon Peyton Jones.

Reusing just that part of the list-comprehension syntax seems to work for multi-part function definitions:

clunky env var1 var2 | Just val1 <- lookup env var1,
                       Just val2 <- lookup env var2  = val1 + val2
                     | otherwise = var1 + var2

but when it's used in case expressions:

 -- ghc-8.6.5/compiler/ghci/RtClosureInspect.hs, lines 1099-1107
check2 (_, rtti_ty) (_, old_ty)
  | Just (_, rttis) <- tcSplitTyConApp_maybe rtti_ty
  = case () of
      _ | Just (_,olds) <- tcSplitTyConApp_maybe old_ty
        -> and$ zipWith check2 (map quantifyType rttis) (map quantifyType olds)
      _ | Just _ <- splitAppTy_maybe old_ty
        -> isMonomorphicOnNonPhantomArgs rtti_ty
      _ -> True
  | otherwise = True

it looks somewhat out-of-place, unless:

map f xs = [ x <- xs -> f x ]

is considered a suitable replacement for:

map f xs = [ f x | x <- xs ]


Could the GHC example benefit from some more list-comprehension syntax? Let's borrow a new reserved word:

check2 (_, rtti_ty) (_, old_ty)
  | Just (_, rttis) <- tcSplitTyConApp_maybe rtti_ty
  = cases
      and $ zipWith check2 (map quantifyType rttis) (map quantifyType olds) |
          Just (_,olds) <- tcSplitTyConApp_maybe old_ty
      isMonomorphicOnNonPhantomArgs rtti_ty |
          Just _ <- splitAppTy_maybe old_ty
      True | otherwise
  | otherwise = True

...well, at least the unit-case workaround is gone. With a little refactoring:

check2 (_, rtti_ty) (_, old_ty)
  | Just (_, rttis) <- tcSplitTyConApp_maybe rtti_ty
  = case () of
      _ | Just (_,olds) <- tcSplitTyConApp_maybe old_ty
        -> and $ zipWith qcheck2 rttis olds
      _ | Just _ <- splitAppTy_maybe old_ty
        -> isMonomorphicOnNonPhantomArgs rtti_ty
      _ -> True
  | otherwise = True

qcheck2 rtti old = check2 (quantifyType rtti) (quantifyType old)

and the alternate syntax:

check2 (_, rtti_ty) (_, old_ty)
  | Just (_, rttis) <- tcSplitTyConApp_maybe rtti_ty
  = cases
      and $ zipWith qcheck2 rttis olds | Just (_,olds) <- tcSplitTyConApp_maybe old_ty
      isMonomorphicOnNonPhantomArgs rtti_ty | Just _ <- splitAppTy_maybe old_ty
      True | otherwise
  | otherwise = True

qcheck2 rtti old = check2 (quantifyType rtti) (quantifyType old)

this particular example is further improved, unless:

filter p xs = [ x <- xs, p x -> x ]

is considered a reasonable substitute for:

filter p xs = [ x | x <- xs, p x ]


Still not convinced? Here's some other examples:

  •  -- ghc-8.6.5/compiler/codeGen/StgCmmMonad.hs, lines 819-827
    let
       (test, then_, else_, likely') = case likely of
         Just False | Just e' <- maybeInvertCmmExpr e
           -> (e', fbranch, tbranch, Just True)
         _ -> (e, tbranch, fbranch, likely)
or:
let
   (test, then_, else_, likely') =
     cases
       (e', fbranch, tbranch, Just True) | Just False <- likely,
                                           Just e' <- maybeInvertCmmExpr e
       (e, tbranch, fbranch, likely)     | otherwise


  •  -- ghc-8.6.5/compiler/basicTypes/Demand.hs, lines 927-934
    splitProdDmd_maybe (JD { sd = s, ud = u })
     = case (s,u) of
         (Str _ (SProd sx), Use _ u) | Just ux <- splitUseProdDmd (length sx) u
                                     -> Just (mkJointDmds sx ux)
         (Str _ s, Use _ (UProd ux)) | Just sx <- splitStrProdDmd (length ux) s
                                     -> Just (mkJointDmds sx ux)
         (Lazy,    Use _ (UProd ux)) -> Just (mkJointDmds (replicate (length ux) Lazy) ux)
         _ -> Nothing
or:
splitProdDmd_maybe (JD { sd = s, ud = u })
 = cases
     Just (mkJointDmds sx ux) | Str _ (SProd sx) <- s, Use _ u <- u,
                                Just ux <- splitUseProdDmd (length sx) u
     Just (mkJointDmds sx ux) | Use _ (UProd ux) <- u, Str _ s <- s,
                                Just sx <- splitStrProdDmd (length ux) s
     Just (mkJointDmds (replicate (length ux) Lazy) ux) |
                                Use _ (UProd ux) <- u, Lazy <- s
     Nothing                  | otherwise
(note the reordering of matches against s and u.)


  •  -- ghc-8.6.5/compiler/specialise/SpecConstr.hs, lines 2209-2215
       mb_scrut dc = case arg_occ of
                       ScrutOcc bs | Just occs <- lookupUFM bs dc
                                   -> Just (occs)
                       _other      | sc_force env || sc_keen env
                                   -> Just (repeat UnkOcc)
                                   | otherwise
                                   -> Nothing
or:
   mb_scrut dc =
     cases
       Just (occs) | ScrutOcc bs <- arg_occ, Just occs <- lookupUFM bs dc
       Just (repeat UnkOcc) | sc_force env || sc_keen env
       Nothing | otherwise
  • One more example:
concatMap f xs = [ x <- xs, y <- f x -> y ]
or:
concatMap f xs = [ y | x <- xs, y <- f x ]

Atravers 01:37, 10 July 2020 (UTC)