4 ms·
The monadic chaining section makes no mention of for-yield strangely enough. Their example def getAddress(name: String): Option[String] = { database.
by whateveracct 12y ago
The monadic chaining section makes no mention of for-yield strangely enough.
Their example
def getAddress(name: String): Option[String] = {
database.get(name).flatMap { elem =>
elem.data.get("address")
.flatMap(Option.apply) // handle null value
}
}
could just be
def getAddress(name: String): Option[String] = {
for {
elem <- database.get(name)
address <- elem.data.get("address")
} yield address
}
and it's way more readable than their given rewrite.
Then again, for-yield might be too magical and "can confuse programmers less familiar with Scala," which seems to be a reason for a lot of the stylistic decisions.
- jghn 12y agoWhile I disagree I've seen people object to the for/yield on monads because "it doesn't really seem like a for loop". YMMV
- azinman2 12y agoI would agree on this one -- it's part of the language that I actively avoid. Especially for anyone with a background in Python it's super confusing.
- whateveracct 12y agoCan people really not decouple the words "for" and "yield" from other languages and just understand how a scala for-yield is desugared? Writing Scala and avoiding for-yield for the reason of it being superficially confusing to someone from another language doesn't really make sense to me.
- azinman2 12y agoImagine if we also changed "while," "return" (which btw scala did), or "if" and made it do something different or similar. Would you still feel that way? Part of the reasons people can learn new langagues "easily" is that there are universal concepts. When you don't change words but change the meaning then that creates unnecessary difficulty. In my opinion if you want to introduce a new concept, also introduce a new vocabulary to address it. We should strive for programming to be easier and more user friendly, and things like this do the opposite. Especially for people like me who switch between multiple languages in a day, it can be mentally taxing to make that context switch. I've never understood ppl on HN defending things being optionally harder. Such is the CS mindset.
- jghn 12y agoCompletely agree w/ you here in terms of language design. OTOH what's done is done and I don't think one needs to eschew an idiomatic concept just because the people who designed it suck at naming. Another example is 'case class', because of its heritage I have colleagues who insist it should only be used for pattern matching purposes. I strongly suspect that if the designers were to do things over again that what we know as 'case class' would just be called 'class'
- azinman2 12y agoI haven't heard about "it should only be used for pattern matching." Is there a performance reason or something more stylistic? I use them all the time because of all the convienence associated with them. I'm not really aware of the downsides except for the ~22 parameter limit (I hit it when deserializing JSON that represents many possible Option(al) keys... And named parameters are awesome).
- the_af 12y agoThat's because it's not a for loop. Maybe the problem is that it's inconsistently named. I like for/yield because it reminds me of do-notation in Haskell, and it's used for a similar purpose.
- jghn 12y agoI agree that it's confusingly named, what I meant was that I disagree w/ those colleagues who avoid it like the plague because of the confusing name. I feel like it's one of those things that seems weird the first time you see it but idioms are idioms.
- kod 12y agoI agree with your point in general. However, in this particular case, what you wrote isn't equivalent. The explicit use of flatMap(Option.apply) in the first example is to deal with the case of address existing, but being a null string. Your code doesn't handle that.
- whateveracct 12y agoGood point I was sloppy in my reading. def getAddress(name: String): Option[String] = { for { elem <- database.get(name) nullableAddress <- elem.data.get("address") address <- Option(nullableAddress) } yield address } def getAddress(name: String): Option[String] = { for { elem <- database.get(name) address <- elem.data.get("address").flatMap(Option.apply) } yield address } are both still better though. Also it seems like a bit of a strawman to use such a pathological example (null values stored in a map are frowned upon even in Java-land) to motivate the entire styleguide section about monadic chaining. They say it's contrived but still -.-
- ilikerps 12y agoNote that while frowned upon, such issues arise altogether more often than they should in real systems.
- nitishmd 12y agoIsn't is better to use async/await over for comprehension, since its more clear and easy to understand. Edit: My bad just did my scala assignment, overlooked the details and thought it was a Future :)
- saryant 12y agoNothing in that example was asynchronous.
- eranation 12y agoMy favorite syntax sugar in Scala. Especially in future composition. Is there a good theoretical (not technical) reason why we can't mix types in a for-yield structure? There is an implicit conversion from Option to Seq added, so I can mix lists and options, but is there a fundamental issue preventing mixing other types? e.g. in the basic math of it?
- runT1ME 12y agoAll for comprehension is syntactic sugar for repeated calls to flatMap. So for { a <- someOption b <- someOtherOPtion } yield a + b //returns Option[Int] ends up being someOption.flatMap(a => someOtherOption.map(b => a+b) ) and remember, all flatMap does is 'map', (so you'd get Option[Option[Int]), then "flatten" it so you end up with just Option[Int]. So if you have 4 'nestings', you only get one level of Option and not 4. The signature for all flatMaps is ... class Something[A] { def flatMap[B](f: A => Something[B]): Something[B] } So, you could have that for Option, for Future, for NonEmptyList, Stream, etc. But they can't mix and match cause you don't necessarily know how to 'flatten' a List into a future. List[Future[A]] is something very different than say just a List[A]. (One is a concurrent computation). So, yes, you can do implicit conversions to alleviate this, or you can do what is known as a 'monad transformer' which sounds SUPER scary but I go over them in some slides and they're really easy to use. https://speakerdeck.com/vmarquez/using-scala-futures-the-functional-way https://speakerdeck.com/vmarquez/using-scala-futures-the-fun... If you want to get into the nitty gritty of how flatMap works and how to mix up futures/options.
- rxin 12y agoThanks -- internally we are having a lot of debate about for comprehension. In particular, the use of it might simplify certain use cases (especially async with futures). That said, it also often leads to very high-entropy, dense code and thus hard to reason about. We will include more guidelines on this in future iterations of the guide.