ekmett/lens

Improve `rewriteM` docs

Opened this issue · 0 comments

Hi! I suggest to improve the docs of rewriteM.

Namely, rewrite:

Rewrite by applying a monadic rule everywhere you can.
Ensures that the rule cannot be applied anywhere in the result.
+ When you want to apply the rule to a result, wrap the result in `Just`.
+ Otherwise, return `Nothing`.

The text below is for context.
The working example may be included into docs.

Goal

The goal is to insert Bottom with a counter into each Object binding that doesn't contain a Bottom.

Attempt 1

This example hangs indefinitely.

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}

module Language.EO.Phi.Ex where

import Control.Lens (Plated, rewriteM, (+=))
import Control.Monad.State (evalState, get)
import Data.Data (Data)

newtype Object = Object [Binding] deriving (Data, Show)
data Binding = Binding Object | Bottom Int deriving (Data, Show)
instance Plated Object
instance Plated Binding

ex1 :: Object
ex1 =
    evalState
        ( rewriteM
            ( \x@(Object bindings) -> do
                cnt <- get
                id += 1
                let isBottom = \case Bottom _ -> True; _ -> False
                pure
                    $ Just
                        ( if not (any isBottom bindings)
                            then Object (Bottom cnt : bindings)
                            else x
                        )
            )
            (Object [Binding (Object [])])
        )
        3

-- >>> ex1
-- ProgressCancelledException

Attempt 2

This example works well.

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}

module Language.EO.Phi.Ex where

import Control.Lens (Plated, rewriteM, (+=))
import Control.Monad.State (evalState, get)
import Data.Data (Data)

newtype Object = Object [Binding] deriving (Data, Show)
data Binding = Binding Object | Bottom Int deriving (Data, Show)
instance Plated Object
instance Plated Binding

ex1 :: Object
ex1 =
    evalState
        ( rewriteM
            ( \(Object bindings) -> do
                let isBottom = \case Bottom _ -> True; _ -> False
                if not (any isBottom bindings)
                    then do
                        cnt <- get
                        id += 1
                        pure $ Just (Object (Bottom cnt : bindings))
                    else pure Nothing
            )
            (Object [Binding (Object [])])
        )
        3

-- >>> ex1
-- Object [Bottom 4,Binding (Object [Bottom 3])]