问题
The flatMap method of the Success is implemented like this:
def flatMap[U](f: T => Try[U]): Try[U] =
try f(value)
catch {
case NonFatal(e) => Failure(e)
}
I kinda understand what this method is doing, it helps us to avoid writing a lot of catching code.
But in what sense is it similar to the regular flatMap?
A regular flatMap takes a sequence of sequences, and put all the elements into one big "flat" sequence.
But the flatMap method of Try is not really flattening anything.
So, how to understand the flatMap method of Try?
回答1:
Without entering into monads, instead of thinking about it in terms of collections, you could think of it in terms of structures (where a collection becomes a structure with many entries).
Now, take a look at the signature of Try.flatmap
(from your post):
def flatMap[U](f: T => Try[U]): Try[U]
the function f
transforms T into a Try[U] in the context of Try[T].
In contrast, imagine the operation were 'map', the result would be:
def badMap[U](f: T => Try[U]): Try[Try[U]]
As you can see, flatmap is 'flattening' the result into the context of Try[T] and producing Try[U]
instead of the nested Try[Try[U]]
.
You can apply the same 'flattening of nested structure' concept to collections as you mention.
回答2:
You may consider Try[T] as similar to a collection of only one element (like Option[T]) .
When the "sequence of sequences" is "only one sequence", map and flatmap are almost similar. Only difference being the signature of the function.
No flattening is required in this case.
回答3:
I found Dan Spiewak's "Monads Are Not Metaphors" very helpful in getting my head around monads. For folks starting from Scala (like me) it's far easier to grasp than anything else I've found - including Odersky's writings. In reading it, note that 'bind'=='flatMap'.
回答4:
As you can read in A Tour of Scala: Sequence Comprehensions:
"In scala every datatype that supports the operations filter, map, and flatMap (with the proper types) can be used in sequence comprehensions." In fact this means you can threat it like a monad.
And flatMap for monad have signature like this:
def flatMap(f: A => M[B]): M[B]
All collections in scala have monadic interfaces, so you can look at monadic operations in that narrow scope as operations on sequences. But that is not the whole story. In case of some monads looking on them as on collections is more confusing than helpful. Generally flatMap applies a transformation of the monad "content" by composing this monad with an operation resulting in another monad instance of the same type. So you can look at monads in at least two ways:
- Monad is some kind of collection (or box holding something) and elements of that collection are "content".
- Monad is some kind of context and elements of monad are just some computations made in that context.
Sometimes it's easier to think about monad as collection, sometimes it's easier to think about it as context. At least for me. In fact that both approaches are interchangeable, i.e. you can look at lists (collections) as nondeterministic computations which may return an arbitrary number of results.
So in case of Try it could be easier to think about it as execution context, with two states, Success and Failure. If you want to compose few Tries then and one of them is in Failure state then whole context becomes Failure (chain is broken). Otherwise you can do some operations on the "content" of that Tries and the context is Success.
回答5:
A regular flatMap takes a sequence of sequences, and put all the elements into one big "flat" sequence.
Slight correction:
A regular flatMap
takes a sequence (more generally monad) , has an argument which is a function converting an element into a sequence (monad), and returns a "flat" sequence (monad).
For comparison purposes, the gory substeps mentioned here :). The flatmap
method iterates over input sequence invoking f(element)
, but creates a singular new result sequence. The "flatten" part is applied after each function argument application, f(element)
- it does a nested iteration over the resulting sub-sequence, yielding each entry in the singular result sequence.
The equivalent for Success
, with a value
inside (more generally a monad):
flatmap
has an argument which is a function convertingSuccess
intoTry
=Success(value)
ORFailure(exception)
. Afterf(value)
is applied, the result is already aTry
. The "flatten" part is a trivial/null operation: iterating over this function result would give just one entry, henceTry
/Success
/Failure
don't even need to implementIterable
). Doesn't wrap additional layers ofSuccess
/Failure
, and so returns a "flat"Try
.I.e. The "flat" part means it doesn't cascade
Success
/Failure
wrappers, just as a sequence'sflatmap
doesn't cascade sequences in a (value tree) hierarchy.this is different to
map
, whose argument is a function convertingSuccess
into an arbitrary typeU
; afterf(value)
is applied, map must add an additional layer of newSuccess
/Failure
wrapping around thevalue
/exception
.
回答6:
A regular flatMap takes a sequence of sequences, and put all the elements into one big "flat" sequence
It would be fair to replace word sequence to monad here, because this operation doesn't relate only to collection, actually collections are also monads. Think of Try
as collection that can contain either Success
value of Failure
来源:https://stackoverflow.com/questions/20195544/scala-how-to-understand-the-flatmap-method-of-try