Scala has the trait Iterable[A] that defines
def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Iterable[B]
That certainly looks
The answer to your headline question is no. A collection with flatMap is not sufficient to be a monad. It might be a monad if it satisfies some further conditions.
Your "minor" issue certainly breaks the monadicity (the proper word for "monad-ness") of Iterable. This is because many subtypes of Iterable and GenTraversableOnce are not monads. Therefore, Iterable is not a monad.
Your "major" issue is not a problem at all. For example, the function argument to the List monad's flatMap receives the elements of the List one at a time. Each element of the list generates a whole list of results, and those lists are all concatenated together at the end.
Fortunately, judging whether something is a monad is really easy! We just have to know the precise definition of monad.
F[_] that takes one type argument. For example, F could be List, Function0, Option, etc.A and produces a value of type F[A].A => F[B], and a function of type B => F[C] and produces a composite function of type A => F[C].(There are other ways of stating this, but I find this formulation straightforward to explain)
Consider these for Iterable. It definitely takes one type argument. It has a unit of sorts in the function Iterable(_). And while its flatMap operation doesn't strictly conform, we could certainly write:
def unit[A](a: A): Iterable[A] = Iterable(a)
def compose[A,B,C](f: A => Iterable[B],
g: B => Iterable[C]): A => Iterable[C] =
a => f(a).flatMap(g)
But this does not make it a monad, since a monad additionally has to satisfy certain laws:
compose(compose(f, g), h) = compose(f, compose(g, h))compose(unit, f) = f = compose(f, unit)An easy way to break these laws, as lmm has already pointed out, is to mix Set and List as the Iterable in these expressions.
While a type construction with just flatMap (and not unit), is not a monad, it may form what's called a Kleisli semigroupoid. The requirements are the same as for monad, except without the unit operation and without the identity law.
(A note on terminology: A monad forms a Kleisli category, and a semigroupoid is a category without identities.)
Scala's for-comprehensions technically have even fewer requirements than semigroupoids (just map and flatMap operations obeying no laws). But using them with things that are not at least semigroupoids has very strange and surprising effects. For example, it means that you can't inline definitions in a for-comprehension. If you had
val p = for {
x <- foo
y <- bar
} yield x + y
And the definition of foo were
val foo = for {
a <- baz
b <- qux
} yield a * b
Unless the associativity law holds, we cannot rely on being able to rewrite this as:
val p = for {
a <- baz
b <- qux
y <- bar
} yield a * b + y
Not being able to do this kind of substitution is extremely counterintuitive. So most of the time when we work with for-comprehensions we assume that we're working in a monad (likely even if we're not aware of this), or at least a Kleisli semigroupoid.
But note that this kind of substitution does not work in general for Iterable:
scala> val bar: Iterable[Int] = List(1,2,3)
bar: Iterable[Int] = List(1, 2, 3)
scala> val baz: Iterable[Int] = Set(1,2,3)
baz: Iterable[Int] = Set(1, 2, 3)
scala> val qux: Iterable[Int] = List(1,1)
qux: Iterable[Int] = List(1, 1)
scala> val foo = for {
| x <- bar
| y <- baz
| } yield x * y
foo: Iterable[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
scala> for {
| x <- foo
| y <- qux
| } yield x + y
res0: Iterable[Int] = List(2, 2, 3, 3, 4, 4, 3, 3, 5, 5, 7, 7, 4, 4, 7, 7, 10, 10)
scala> for {
| x <- bar
| y <- baz
| z <- qux
| } yield x * y + z
res1: Iterable[Int] = List(2, 3, 4, 3, 5, 7, 4, 7, 10)
For more on monads in Scala, including what it all means and why we should care, I encourage you to have a look at chapter 11 of my book.