alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (ReaderWriterStateTUsage.scala)

This example Scala source code file (ReaderWriterStateTUsage.scala) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Scala by Example" TM.

Learn more about this Scala project at its project page.

Java - Scala tags/keywords

cabrunlengthencoder, cord, int, list, option, readerwriterstatet, readerwriterstatetusage, runlength, runlengthconfig, runlengthstate, show, string, token

The ReaderWriterStateTUsage.scala Scala example source code

package scalaz.example

/*
 A rather contrived example which shows how ReaderWriterStateT could be used.
 @author stew@vireo.org / stew@helloreverb.com

 We create a simple langauage named CAB
 Strings in the CAB language is a series of A, B, or C tokens followed by a EOF:
 <cab> ::= 'C' | 'A' | 'B'
 <string> ::=   | '.'


 So valid strings in the langauge would be:
 "."
 "A."
 "B."
 "ABC."
 "AAAABBCABC."
 */

import scalaz._
import Scalaz._

sealed trait Token
case object C extends Token
case object A extends Token
case object B extends Token

object Token {

  implicit val euqalsRef: Equal[Token] = Equal.equalRef
  implicit val showTok: Show[Token] = new Show[Token] {
    override def show(t: Token) = t match {
      case A => Cord("A")
      case B => Cord("B")
      case C => Cord("C")
    }
  }
}


object CABRunLengthEncoder {
  /*
    A run-length encoder for CAB, a token T can be prefixed by a number N to stand for "N Ts in a row":

   <cab> ::= 'C' | 'A' | "B'
   <tok> ::=  |  
   <compressed> ::=   | "."

   so the string "CAAAAAB." could be "compressed" any of the following ways:
   "CAAAAAB."
   "C5AB."
   "C3AAAB."
   "1C5CB."
   */

  // The State we will carry in the Monad during our computation
  case class RunLengthState(
    lastToken: Option[Token],  // what is the last char we've seen
    length: Int,               // how many of that char have we seen
    input: List[Token]         // remaining input
  ) {
    def incLength = this.copy(length = length + 1)
    def newToken(t: Token) = RunLengthState(Some(t), 0, input)
    def uncons: (Token, RunLengthState) = (input.head, this.copy(input = input.tail))
  }
  object RunLengthState {
    def initial(input: List[Token]) = RunLengthState(None,0, input)
  }

  /**
    * the configuration of our encoder, this will be used in the
    * Reader part of our RWST    */
  case class RunLengthConfig(
    /**
      * if we are emitting less than minRun tokens, just emit them as
      * individual tokens instead of as a run of tokens
      */
    minRun: Int
  )

  import Token._
  import Free.Trampoline

  type RunLength[A] = ReaderWriterStateT[Trampoline, RunLengthConfig, Cord, RunLengthState, A]

  // At its essence the RWST monad transformer is a wrap around a function with the following shape:
  // (ReaderType, StateType) => Monad[WriterType, Result, StateType]
  // we can directly wrap a function of this shape:
  /**
    *  read a token from the input
    */
  val readToken: RunLength[Token] = ReaderWriterStateT { (config: RunLengthConfig, oldState: RunLengthState) =>
    val (nextTok, newState) = oldState.uncons
    Applicative[Trampoline].point((Monoid[Cord].zero, nextTok, newState))
  }

  // Bring the RWST syntax into scope.
  // This will bring into scope methods which return things of type RunLength[_].
  // example syntax methods:
  // get    -- gets the current state
  // put    -- replaces the current state
  // modify -- alter the current state
  // tell   -- append to the writer
  // ask    -- read from the reader
  val rle = ReaderWriterStateT.rwstMonad[Trampoline, RunLengthConfig, Cord, RunLengthState]
  import rle._

  /**
    * with the above syntax imported, we can perform the same
    * computation as above, but use a for comprehension
    */
  val readToken2: RunLength[Token] =
    for {
      oldState <- get            // fetch the current state

                                // take a token off the input, getting
                                // a token and a new state
      (nextTok, newState) = oldState.uncons
      _ <- put(newState)         // store the new state
    } yield nextTok             // return the token

  /**
    have we exhausted the input?
    */
  def done: RunLength[Boolean] =
    get flatMap { state =>
      if(state.input.isEmpty)
        // we have, better emit whatever tokens are stored in the
        // current state
        emit as true
      else
        point(false)
    }

  /**
    * put output on the writer
    */
  def writeOutput(token: Token, length: Int, minRun: Int): RunLength[Unit] =
    if(length <= minRun)
      tell(Monoid[Cord].multiply(token.show, length))
    else
      tell(length.show ++ token.show)


  /**
    emit the lastToken
    */
  def emit: RunLength[Unit] =
    for {
       state <- get
      config <- ask
           _ <- state.lastToken.cata(none = point(()),  // nothing to emit
                                    some = writeOutput(_, state.length, config.minRun))
    } yield ()


  /**
    emit tokens if the next input token is different than the last
    */
  def maybeEmit: RunLength[Unit] =
    for {
      state <- get
       next <- readToken
           _ <- { if(state.lastToken.map(_ == next) getOrElse(false))
                 // Same token as last, so we just increment our counter
                 modify(_.incLength)
               else
                 // its a new token, so emit the previous, then change
                 // the token in the state.
                 // *> here chains two actions, ignoring the output of
                 // the first (which is unit in this case)
                 emit *> put(state.newToken(next))
          }
    } yield ()

  def encode(minRun: Int, input: List[Token]): String = {
    val config = RunLengthConfig(minRun)
    val initialState = RunLengthState.initial(input)
    val (output, result, finalState) = untilM_(maybeEmit, done).run(config, initialState).run
    output.shows
  }
}

object ReaderWriterStateTUsage extends App {

  val inputTokens = List(A,B,C,A,A,B,B,B,B,C,A,A,A,C,C,C,C,B)
  val encoded = CABRunLengthEncoder.encode(2, inputTokens)

  println("encoded " + inputTokens.mkString  + " as " + encoded)
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala ReaderWriterStateTUsage.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.