Skip to content

Instantly share code, notes, and snippets.

@stew
Last active August 29, 2015 14:01
Show Gist options
  • Save stew/5925f8b26117e5b27ca8 to your computer and use it in GitHub Desktop.
Save stew/5925f8b26117e5b27ca8 to your computer and use it in GitHub Desktop.
import scalaz._
import Scalaz._
object Main extends App {
// two lists we'll be traversing
val nonRepeating = List(1,2,3,4)
val repeating = List(1,2,3,3,4)
// so we can use state to traverse a list
val checkForRepeats: Int ⇒ State[Option[Int], Boolean] = { next ⇒
import State._
for {
last ← get
_ ← put(some(next))
} yield (last === some(next))
}
val res1: List[Boolean] = Traverse[List].traverseS(nonRepeating)(checkForRepeats).eval(None)
val res2: List[Boolean] = Traverse[List].traverseS(repeating)(checkForRepeats).eval(None)
assert(res1.foldMap(Tags.Disjunction(_)) == false)
assert(res2.foldMap(Tags.Disjunction(_)) == true)
// Writer and Reader Functors form and adjuntion that gives us a
// Monad the behaves like State, think of it as a function which
// Reads the state perfoms a computation and Writes the new state.
type RWState[S,A] = Reader[S, Writer[S, A]]
// here is our checkForRepeats function rewriten for the new form
val checkForRepeatsAdj: Int ⇒ RWState[Option[Int], Boolean] = { next ⇒
Reader(last ⇒ Writer(some(next), last === some(next)))
}
val adjOptionInt = Adjunction.writerReaderAdjunction[Option[Int]]
val didTheyRepeat : Boolean = {
implicit val adjMonad = adjOptionInt.monad
val (_, bools) = nonRepeating.traverseU(checkForRepeatsAdj).run(None).run
bools.foldMap(Tags.Disjunction(_))
}
assert(didTheyRepeat == false)
val didTheyRepeat2 : Boolean = {
implicit val adjMonad = adjOptionInt.monad
val (_, bools) = repeating.traverseU(checkForRepeatsAdj).run(None).run
bools.foldMap(Tags.Disjunction(_))
}
assert(didTheyRepeat2 === true)
// here's another stateful computation, it simply carries a sum
// along in as the State, returning Unit. This could clearly be done
// more simply as a fold, but we'll use this again later more
// meaningfully
val adjInt = Adjunction.writerReaderAdjunction[Int]
val sum: Int ⇒ RWState[Int,Unit] = { next ⇒
Reader(last ⇒ Writer(last + next, ()))
}
val sumOfInts = {
implicit val adjMonad = adjInt.monad
nonRepeating.traverseU(sum).run(0).run._1
}
assert(sumOfInts === 10)
// the Id functors get in the way of type inference
// TODO: see if we can get rid of this explicitness
// also see if we can Trampoline these as they are going to blow the
// stack for a long traversal
type ROIRIWW[A] = Reader[Option[Int],
Reader[Int,
Writer[Int,
Writer[Option[Int],A]]]]
// now we can combine our two stateful computations
val checkForRepeatsAdjAndSum2: Int ⇒ ROIRIWW[Boolean] = { next ⇒
checkForRepeatsAdj(next).map(w ⇒ sum(next).map(_.map(_ ⇒ w)))
}
// combine two arbitrary RWState computations with a function to map their results to a single result
def run2RWState[A,S1,S2,B,C,R](rws1: A ⇒ RWState[S1,B], rws2: A ⇒ RWState[S2,C], f: (B,C) ⇒ R) = { a: A ⇒
rws1(a).map(b ⇒ rws2(a).map(_.map(c ⇒ Writer(b.run._1,f(b.run._2,c)))))
}
// with the above function we can combine the two stateful computations with a function that throws away the Unit from sum
val checkForRepeatsAdjAndSum: Int ⇒ ROIRIWW[Boolean] = run2RWState(checkForRepeatsAdj,sum,(a:Boolean,_:Any) ⇒ a)
// since the adjunctions compose, we can run both stateful computations with a single traverse of the list
val bothAtOnce = {
// yay for type scala's inference :(
implicit val adjMonad = adjOptionInt.compose[({type λ[A]=Writer[Int,A]})#λ,({type λ[A]=Reader[Int,A]})#λ](adjInt).monad
nonRepeating.traverseU(checkForRepeatsAdjAndSum).run(None).run(0).run
}
val repeats : Boolean = bothAtOnce._2.run._2.foldMap(Tags.Disjunction(_))
val sumResult : Int = bothAtOnce._1
assert(repeats === false)
assert(sumResult === 10)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment