Removing syntactic sugar: List comprehension in Haskell

后端 未结 4 1041
星月不相逢
星月不相逢 2020-12-03 14:43

Can I unsugar list comprehension in this expression:

[(i,j) | i <- [1..4], j <- [i+1..4]]

This is the output:

[(1,2),         


        
相关标签:
4条回答
  • 2020-12-03 14:50
    concatMap (\i -> map (\j -> (i, j)) [i+1 .. 4]) [1 .. 4]
    
    0 讨论(0)
  • 2020-12-03 14:51

    There is yet another translation scheme, it is due to Wadler, as far as I know.

    This would give:

    let
        lc_outer (x:xs) = let lc_inner (y:ys) = (x,y) : lc_inner ys
                              lc_inner []     = lc_outer xs
                          in lc_inner [x+1.. 4]
        lc_outer [] = []
    in  lc_outer [1..4]
    

    This translation avoids needless construction of singleton lists in the innermost level that would need to get flattened with concatMap later.

    0 讨论(0)
  • 2020-12-03 15:09

    List comprehensions (in fact, Monad comprehensions) can be desugared into do notation.

    do i <- [1..4]
       j <- [i+1..4]
       return (i,j)
    

    Which can be desugared as usual:

    [1..4]   >>= \i ->
    [i+1..4] >>= \j ->
    return (i,j)
    

    It is well known that a >>= \x -> return b is the same as fmap (\x -> b) a. So an intermediate desugaring step:

    [1..4] >>= \i -> 
    fmap (\j -> (i,j)) [i+1..4]
    

    For lists, (>>=) = flip concatMap, and fmap = map

    (flip concatMap) [1..4] (\i -> map (\j -> (i,j) [i+1..4])
    

    flip simply switches the order of the inputs.

    concatMap (\i -> map (\j -> (i,j)) [i+1..4]) [1..4]
    

    And this is how you wind up with Tsuyoshi's answer.


    The second can similarly be desugared into:

    concatMap (\i ->
      concatMap (\j ->
        map       (\k ->
          (i,j,k))
        [j+1..6])
      [i+1..6])
    [1..6]
    
    0 讨论(0)
  • 2020-12-03 15:11

    The desugared code is:

    concatMap (\i -> concatMap (\j -> (i, j) : []) [i+1..4]) [1..4]
    

    Which can be refactored to Tsuyoshi Ito's answer.

    0 讨论(0)
提交回复
热议问题