for {
comprehensions <- Scala Night @ Stash, May 22nd, 2018
} yield [fun](https://github.com/ASRagab/for-comprehensions-for-fun)
private def fromPath(basePath: BasePath)(pathDef: (String, PathItem)) = {
implicit val (url, path) = pathDef
for {
operationName <- path.operationNames
verb <- verbFromOperationName(operationName)
operation = path.operation(operationName)
namePrefix = base / "paths" / url / operationName
astPath = uriFragmentToReference(url)
params = parameters(path, operation, namePrefix)
handlerCall <- handler(operation, path, params, operationName, astPath).toSeq
(types, states) = resultTypes(namePrefix, operation)
(mimeIn, mimeOut) = mimeTypes(operation)
errMappings = errorMappings(path, operation)
security = securityRequirements(operation)
} yield ApiCall(verb, Path(astPath), handlerCall, mimeIn, mimeOut, errMappings, types, states, security.toSet)
}
//Scala has "traditional" for loops
for (x <- List(1, 2, 3)) {
print(x)
}
//Outputs:
123
@[1](The for loop is not an expression but evaluates an expression 0..n
times based on an iteration condition)
val x = Some(10)
val y = Some(20)
val z = Some(30)
val result = for {
i <- x
j <- y if j < 21
k <- z
t = i * j
} yield i + j + k - t
@[5](the return type of result is Option[Int]
)
In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language "sweeter" for human use: things can be expressed more clearly, more concisely...
Using desugar in IntelliJ
x.flatMap((i: Int) => y.withFilter((j: Int) => j < 21)
.flatMap((j: Int) => z.map { k: Int =>
val t = i * j;
Tuple2(k, t) }
.map({ case (t, k: Int) => i + j + k - t })))
x.flatMap { i =>
y.withFilter(j => j < 21).flatMap {j =>
z.map { k =>
val t = i * j
(k, t)
}.map { case (k, t) => i + j + k - t }
}
}
-
Left Arrow (
<-
) typically callsflatMap
-
Equal signs are a basic assignment to
val
-
if
at the end of a line is afilter
orwithFilter
-
yield
at the end calls map
At the beginning of a comprehension if you start with a List
, you will get typically get back a List
, if you started with an Option
, you will usually get back an Option
case class Postcard(msg: String, landmark: String, recipient: String)
case class Sender(name: String)
val landmarks = List("Grand Canyon", "Niagra Falls", "America's Largest McDonald's")
val recipients = List("Mom", "Dad", "Uncle Robert", "Aunt Leslie")
val senders = List(Sender("TJ"), Sender("Samantha"))
var postcardsBuffer = List[Postcard]()
for(sender <- senders) {
for(recipient <- recipients) {
if(recipient != "Aunt Leslie") {
for(landmark <- landmarks) {
val msg = s"Dear $recipient, Wish you were here at the $landmark with me, miss you! Love, ${sender.name}"
val postcard = Postcard(msg, landmark, recipient)
postcardsBuffer ::= postcard
}
}
}
}
@[8](Note here the need to mutate state, the list of postcards has to be updated inside the nested for loop)
val postcards = for {
sender <- senders
recipient <- recipients if recipient != "Aunt Leslie"
landmark <- landmarks
msg = s"Dear $recipient, Wish you were here at the $landmark with me, miss you! Love, ${sender.name}"
} yield Postcard(msg, landmark, recipient)
@[1-6](While the code isn't much shorter in this case, it is much flatter, and no mutable state)
var tokens = from l in text
from w in l.Split(' ') // Will resolve to SelectMany, which is flatMap
where w.startsWith('a') // if or filter
select w; // yield or map
tokens = do
l <- lines text
ws <- words l --flatMap
w <- filter ('a' `isPrefixOf`) ws --filter obvs
return w --return is like yield NOT like C-Style return
Monadic comprehensions allow you to sequence a series of operations on values inside a context
without dealing particularly with that context. The for-yield
notation and its relatives, allow you to operate
in that context without dealing particularly with the fact that it's a Monad.