scala notes: monoids and for-comprehensions working together
It has taken me awhile to understand monoids and their use. Once you get the hang of it, they make a lot of sense as a design pattern for structuring your code. Either, Option, Try are all monoids that can help structure your code and adapt existing code without changing the existing code or they help in structuring new code.
It also took a while to understand for-comprehensions as well. It turns out that switching monoids types in the middle of a for-comprehension is not impossible but difficult. Its best if each statement in the for-comprehension has the same monoid return type.
On gisthub: https://gist.github.com/aappddeevv/6530787 in case it does not display below correctly.
It also took a while to understand for-comprehensions as well. It turns out that switching monoids types in the middle of a for-comprehension is not impossible but difficult. Its best if each statement in the for-comprehension has the same monoid return type.
On gisthub: https://gist.github.com/aappddeevv/6530787 in case it does not display below correctly.
1:
2: // Define functions that return an Option
3: def f1(x: Int): Option[Int] = Some(x*30)
4: def f2(x: Int): Option[Int] = Some(x+3)
5: def f3(x: Int): Option[Int] = Some(x*2)
6:
7: // Sequence the computation, it will be successful
8: var answer = for {
9: x <- f1(30)
10: y <- f2(x)
11: z <- f3(y)
12: } yield (x,y,z)
13:
14: println("answer: " + answer)
15:
16: // Sequence the computation, the filter will be used
17: answer = for {
18: x <- f1(30)
19: y <- f2(x) if(x>1000)
20: z <- f3(y)
21: } yield (x,y,z)
22:
23: println("answer with filter: " + answer)
24:
25: // Define functions that return Either
26: // Note that e2b returns Left as if it is an error.
27: def e1(x: Int): Either[String, Int] = Right(x*30)
28: def e2(x: Int): Either[String, Int] = Right(x+3)
29: def e2b(x: Int): Either[String, Int] = Left("error")
30: def e3(x: Int): Either[String, Int] = Right(x*2)
31:
32: // Sequence the computations, knowing it will succeed
33: var eanswer = for {
34: x <- e1(30).right
35: y <- e2(x).right
36: z <- e3(y).right
37: } yield z
38:
39: println("eanswer: " + eanswer)
40:
41: // Sequence computations, knowing that it will have an error
42: eanswer = for {
43: x <- e1(30).right
44: y <- e2b(x).right
45: z <- e3(y).right
46: } yield z
47:
48: println("eanswer (with Left()): " + eanswer)
49:
50:
51: /** Prints:
52: answer: Some((900,903,1806))
53: answer with filter: None
54: eanswer: Right(1806)
55: eanswer (with Left()): Left(error)
56: **/
57:
58: // The following does not compile
59: // eanswer = for { ... y <- e2b(x).left ... } ...
60: // Because e3, the function after e2b is called expects an int,
61: // but Left contains a String so when it is unwrapped to call
62: // e3 it is the wrong type. Scala compile time checks catch this!
63:
64: // Either, Left and Right do not have a withFilter method
65: // so you cannot use if() clauses in for comprehensions directly
66: // when using Either. Option has a withFilter method.
67: println("Filter on <200: " + e2(33).right.filter[String](_<200))
68: println("Filter on >200: " + e2(33).right.filter[String](_ > 200))
69:
70: /** Prints:
71: Filter on <200: Some(Right(36))
72: Filter on >200: None
73: **/
74:
75: // Sequencing using just the Either monad and no filter
76: // criteria used on any of the map lines.
77: // Generally, when using a filter, check the scala
78: // docs for the monoid you are using (e.g. Option or
79: // Either) to understand what map, flatMap and withFilter
80: // do when applied to the monoid value. If withFilter
81: // is not present, then you may have difficulty using
82: // an if-condition on one of the "map" lines.
83: //
84: // Note that it does not make sense to have a withFilter
85: // on an Either. When using withFilter on an Option monoid,
86: // if the filter selects false (and hence returns a None),
87: // then a Some(yourValue) becomes a None. Both of which are
88: // well defined. But if you use either and the Right(yourValue)
89: // (or left depending on which 1/2 you use) is not selected,
90: // you need to return Left(someOtherYourValue) but someOtherYourValue
91: // is never defined automatically so there is no "automatic"
92: // other value to use. That's why you cannot apply a filter
93: // using the for-comprehension syntax.
94: //
95: // In the for-comprehension below, if a Left appears as a result
96: // of the function call, the for terminates immediately and returns
97: // the Left as the value.
98: var eanswer2 = for {
99: x <- e1(30).right
100: y <- e2(x).right
101: z <- e3(y).right
102: } yield z
103:
104: println("eanswer with no filter: " + eanswer2)
105:
106: /** Prints:
107: eanswer with no filter: Right(1806)
108: **/
109:
Comments
Post a Comment