03-Monad-Writer-Example.purs

module ComputingWithMonads.MonadWriter where

import Prelude
import Effect (Effect)
import Effect.Console (log)
import Data.Identity (Identity(..))
import Data.Tuple (Tuple(..))
import Control.Monad.Writer.Class (tell, listen, pass, listens, censor)
import Control.Monad.Writer (Writer, runWriter)
import Control.Monad.Writer.Trans (WriterT(..))

main :: Effect Unit
main = case runWriter writeStuff of
  Tuple output otherUsefulData -> do
    log $ "Computation's output: " <> show output
    log $ "Other useful data:\n\n\t" <> otherUsefulData

type Output = Int
type OtherUsefulData = String

                 --   w                 a
           -- WriterT String Identity Int
writeStuff :: Writer  String          Int
writeStuff = do
  (Tuple output1 usefulData) <- listen $ WriterT (
        -- A computation that...
        Identity (
          -- returns this entire object as output,
          -- so that one can use both the computed output
          -- and the OtherUsefulData instance in later computations
          Tuple
            -- the output
            1
            -- the instance, which is appended via `tell` to
            -- the current instance of OtherUsefulData
            "useful data"
        )
    )
  tell $ "\n\n\t" <> "(Listen) 'output1' was: " <> show output1
  tell $ "\n\n\t" <> "(Listen) 'usefulData' was: " <> usefulData
      -- needed to improve reading:
      --  turns "usefulDatasomeData" into
      -- "usefulData" <> "\n\n\t" <> "someData"
      <> "\n\n\t"

  Tuple output2 modifiedData <- listens

    -- 2) ... a modified version of the non-output data
    --          which is not appended via `tell`
    --        before exposing it to the do notation
    (\someData -> "Modified (" <> someData <> ")") $
    WriterT (
        -- 1) A computation...
        Identity (
          -- 2) that returns this entire object as output...,
          -- so that one can use both the computed output and...
          Tuple
            -- the output (this is `output2`)
            2
            -- the instance, which is appended via `tell` to
            -- the current instance of OtherUsefulData
            "someData"
        )
    )
  tell $ "\n\n\t" <> "(Listens) two: " <> show output2
  tell $ "\n\n\t" <> "(Listens) modified Data: " <> show modifiedData

  output3 <- pass $ WriterT (
      -- A computation...
      Identity (

        -- that returns 3 things using a nested Tuple
        --    nested tuple: (Tuple (Tuple one three) two)
        Tuple
          (Tuple
            -- 1) the computation's output (this is `output3`)
            4
            -- 3) a function which modifies the non-output data
            --    before appending it to the current non-output data
            --    instance
            (\value -> "\n\n\t" <> "(in `pass`) Value is: " <> value)
          )
          -- 2) the non-output data
          "value"
        )
    )

  tell $ "\n\n\t" <> "(Pass) output2 was: " <> show output3
    -- needed to improve reading:
    --  turns "4someData" into
    -- "4" <> "\n\n\t" <> "someData"
    <> "\n\n\t"

  output4 <- censor
      -- 3) a function which modifies the non-output data
      --    before appending it to the current non-output data
      --    instance
      (\value -> "(in `censor`) Value is: " <> value) $ WriterT (
        -- 1) A computation...
        Identity (
          -- ...that returns two things
          Tuple
            -- a) the computation's output (this is `output4`)
            2
            -- b) the non-output data, which is...
            "someData"
        )
      )

  tell $ "\n\n\t" <> "(censor) output3 was: " <> show output4

  pure 0