-
-
Save travisbrown/867230e3beaa1053281f to your computer and use it in GitHub Desktop.
Sequencing through Monad with ADT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scalaz._, Scalaz._ | |
sealed trait Status | |
case object First extends Status | |
case object Second extends Status | |
case object Third extends Status | |
type StringOr[A] = String \/ A | |
def f = IndexedStateT.constantIndexedStateT[StringOr, Unit, First.type, Unit](())(First) | |
def g = IndexedStateT[StringOr, First.type, Second.type, Unit](_ => (Second, ()).right) | |
def h = IndexedStateT[StringOr, Second.type, Third.type, Unit](_ => (Third, ()).right) | |
for { | |
a <- f | |
b <- g | |
c <- h | |
} yield c | |
/* Doesn't compile: | |
for { | |
a <- g | |
b <- f | |
c <- h | |
} yield c | |
*/ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given the following ADT: | |
sealed trait Status | |
case object One extends Status | |
case object Two extends Status | |
case object Three extends Status | |
// They represent states. The natural progression, for this | |
// example, is : One -> Two -> Three | |
// Then, the functions that'll be used in the below monadic chaining. | |
// Observe that, it's possible, for an error to occur during f, g, or h | |
def f: Either[String, Status] = Right(One) | |
def g(x: Status): Either[String, Status] = Right(Two) | |
def h(x: Status): Either[String, Status] = Right(Three) | |
// Lastly, a for-comprehension that sequences through f, g and h. | |
// Please ignore the fact that g and h both discard their input | |
scala> for { | |
| a <- f.right | |
| b <- g(a).right | |
| c <- h(b).right | |
| } yield c | |
res4: scala.util.Either[String,Status] = Right(Three) | |
// From a type perspectice, `g` could return `Right(One)`. It would not | |
// make sense per this domain (where all we know is One -> Two -> Three), | |
// but of course One, Two and Three constitute Status. | |
// Can I do better here? I wrote a service that's similar to | |
// this example, but I'm finding myself adding tests (with a test service) | |
// where `g` returns Right(One). As a result, `h` will return | |
// Left("Invalid state. Got One, but expected Two.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hey Travis - what about this?
A colleague pointed me to this approach.