questioning FP

2,457 views
Skip to first unread message

Ittay Dror

unread,
Oct 9, 2011, 4:34:47 PM10/9/11
to scala-debate

Hi All,


(I welcome any sort of replies, even containing foul language, as long as there's also information inside.)

I'm about to give a lecture on FP. Now, some concepts in FP I love, but for some I have a hard time finding a good angle. Specifically, the IO monad.

Consider the following imperative code:

  service.fetchData

  service.close

Obviously, both method calls create side effects.

Now, (discarding iteratees), lets consider making this with the IO monad:

  for (data <- service.fetchData;

         _ <- service.close)

     yield data

where all methods are changed to return IO[Data], IO[Unit] and also the calling code must now return an IO[X] for some X.

The question is, what have we gained?

Instead of sequencing by line order, we're explicitly sequencing in the for comprehension. Not much of a gain here, since any mistake that causes code to be written in the first order in the imperative case can also be done in the functional case.

I also don't see anything that makes the code less prone to other mistakes. Such as forgetting to close or calling close twise. Obviously, there are ways to fix this such as service.withData(f: Data => X), but again, the more imperative style of silently calling close inside withData doesn't loose to the functional style that must return IO[X].

What am I missing? I'd appreciate any other examples which show how the use of IO is superior by making code that is more robust.

Regards,

Ittay


martin odersky

unread,
Oct 9, 2011, 4:52:20 PM10/9/11
to Ittay Dror, scala-debate

The IO monad was a neat trick for combining side effects with lazy
evaluation. But your title seems to be wrong. Questioning FP? FP is
much broader than lazy evaluation. In fact, there is only one lazily
evaluated language in wide usage today and even its creators have said
that laziness was probably a mistake.

Strict languages don't need the IO monad, and generally don't have it,
even though they could. Bob Harper's posts in his "existential type"
series are a good explanation on why not.

An interesting quote from Simon Peyton Jones is that "laziness has
kept us pure". That is, it has motivated the use of monads to
represent effects.
I agree that a static representation of effects is very useful, but my
gut feeling is that there must be a more lightweight and polymorphic
way to get effect checking than what monads provide.

-- Martin

Steven Obua

unread,
Oct 9, 2011, 5:20:15 PM10/9/11
to Ittay Dror, scala-debate
You are not missing anything. Monads are really overrated and not essential to FP. 

- Steven

Rex Kerr

unread,
Oct 9, 2011, 5:57:29 PM10/9/11
to Ittay Dror, scala-debate
Hi Ittay,

I also have yet to see any point to IO monads in the vast majority of cases.  They seem to allow one to pay a tiny little bit less attention after everything is set up in exchange for requiring heaps more abstraction* and boilerplate.

For what it's worth, one of the best discussions I've seen of IO monads--with something of a positive spin on them--is from James Iry:
  http://james-iry.blogspot.com/2007/11/monads-are-elephants-part-4.html

Anyway, I find that adhering to four principles yields robust IO.
  (1) Input that may fail should be wrapped in an option or the like, and manipulated functionally (i.e. maps and such on the option).
  (2) Output that may fail should take a lambda whose job it is to deal with the consequences of failure if it cannot be ignored.
  (3) If multiple steps must happen in sequence, access them only through a method that ensures that all steps happen in sequence.  If this is not possible, create a single entity (class, actor, whatever) whose job it is to supervise all the input or output from one channel.

I _do_ have to keep these things in mind.  The framework does not force me to do it this way.  But this doesn't at all diminish the importance of functional programming approaches; it just uses them in a different way than the IO monad.

For example, my personal library contains things like

object Print {
  def toFile(f: File)(out: PrintWriter => Unit) {
      val p = new java.io.PrintWriter(f)
      try { out(p) } finally { p.close() }
    }
  }
  def toFileSafely(f: File)(err: (File,IOException) => Unit)(out: PrintWriter => Unit) {
    // Same deal, but catch exceptions and have err handle them
  }
}

which make it almost impossible to mess up sending text to a file:

val xs = List(1,3,5,7,9)
Print.toFile(new File("test.txt")) { pr => xs.map(pr.println) }

For cases where resources need to be cleaned up, I also have a variant of the forward pipe that looks something like:

class TidyPipe[A](a: A) {
  def tidy[Z](f: A => Z)(g: A => Any) = { val z = f(a); g(a); z }
}

which with you can then (given implicit conversion in scope):

input tidy { parser } { _.close() }

and a well-written parser(in: Input) method should emit an Option[ParsedResult] of some sort, so then this is manipulated with stuff like:

input tidy { parser } { _.close() } map { result => some; complicated; thing(result) }

These sorts of things make it so easy to do the right thing that I really don't miss IO monads; I already spend almost no time thinking about IO (aside from complex things like parsing, and that is because parsing is inherently complex), and it comes out correct essentially all the time (and the mistakes are _not_ because I lost track of what the state of the world was supposed to be).  Adding more of a framework around things would not help.

  --Rex

* Abstraction is great when the essence of your problem remains behind, and irrelevant but distracting details disappear.  However, with IO I find that abstraction makes the essence of the problem disappear while leaving behind necessary but trivial details.  For example, maybe I need to know it's a byte buffer of size 8192 in order to do my parsing!

Jesper Nordenberg

unread,
Oct 9, 2011, 6:31:06 PM10/9/11
to scala-...@googlegroups.com, Ittay Dror, scala-debate
martin odersky skrev 2011-10-09 22:52:
> Strict languages don't need the IO monad, and generally don't have it,
> even though they could. Bob Harper's posts in his "existential type"
> series are a good explanation on why not.
>
> An interesting quote from Simon Peyton Jones is that "laziness has
> kept us pure". That is, it has motivated the use of monads to
> represent effects.
> I agree that a static representation of effects is very useful, but my
> gut feeling is that there must be a more lightweight and polymorphic
> way to get effect checking than what monads provide.

Well, the IO monad (or rather the IO type) is a simple and
straightforward way to keep the language pure without introducing an
additional effects system. So there are certainly benefits to using it
regardless of if the language is lazy or strict.

/Jesper Nordenberg

Meredith Gregory

unread,
Oct 9, 2011, 8:47:43 PM10/9/11
to Jesper Nordenberg, scala-...@googlegroups.com, Ittay Dror
Dear Martin,

It's intriguing to contemplate the possibility of something lighter weight than a three method api!

Best wishes,

--greg
--
L.G. Meredith
Managing Partner
Biosimilarity LLC
7329 39th Ave SW

Ittay Dror

unread,
Oct 10, 2011, 1:16:53 AM10/10/11
to martin odersky, scala-debate



martin odersky wrote:
The IO monad was a neat trick for combining side effects with lazy evaluation. But your title seems to be wrong. Questioning FP? FP is much broader than lazy evaluation. In fact, there is only one lazily evaluated language in wide usage today and even its creators have said that laziness was probably a mistake. Strict languages don't need the IO monad, and generally don't have it, even though they could. Bob Harper's posts in his "existential type" series are a good explanation on why not. An interesting quote from Simon Peyton Jones is that "laziness has kept us pure". That is, it has motivated the use of monads to represent effects. I agree that a static representation of effects is very useful, but my gut feeling is that there must be a more lightweight and polymorphic way to get effect checking than what monads provide. -- Martin

The title is probably too much of a troll. Sorry about that.

My questioning is specifically about making functions pure. And the hoops one need to jump through to get that (at least the way I know which is the IO monad). More specifically:
1. what does it buy me? in theory, pure functions are nice since they behave better, but i want to find a use case that appeals to everyday java developers. That is, a case where making a function pure removes pitfalls (while the function remains basically the same)
2. what are the alternatives to IO monad? an implicit World argument?

Thank you,
Ittay

Lars Hupel

unread,
Oct 10, 2011, 2:39:04 AM10/10/11
to scala-...@googlegroups.com
> 2. what are the alternatives to IO monad? an implicit World argument?

You can also make the "World" argument not implicit; but in any case,
the type system has to ensure that you don't reuse an "old" world. This
is called "linear types", and there is a language which does something
similar ("uniqueness types"): <http://wiki.clean.cs.ru.nl/Clean>

Roland Kuhn

unread,
Oct 10, 2011, 2:45:54 AM10/10/11
to Lars Hupel, scala-...@googlegroups.com

How can this ever work in a distributed concurrent setting? I saw “Concurrent Clean” mentioned on that page, but couldn’t (quickly) find further mention. Where can I dig deeper?


Roland Kuhn
Typesafe – Enterprise-Grade Scala from the Experts
twitter: @rolandkuhn

Jesper Nordenberg

unread,
Oct 10, 2011, 2:49:10 AM10/10/11
to scala-...@googlegroups.com, martin odersky, scala-debate
Ittay Dror skrev 2011-10-10 07:16:
> The title is probably too much of a troll. Sorry about that.
>
> My questioning is specifically about making functions pure. And the
> hoops one need to jump through to get that (at least the way I know
> which is the IO monad). More specifically:
> 1. what does it buy me? in theory, pure functions are nice since they
> behave better, but i want to find a use case that appeals to everyday
> java developers. That is, a case where making a function pure removes
> pitfalls (while the function remains basically the same)

That's basically the same question as "what does a type system buy me?".
It enables automatic detection of programming errors at compile time
rather than at runtime.

It also opens up opportunities for performance optimizations and makes
your code a whole lot easier to test.

> 2. what are the alternatives to IO monad? an implicit World argument?

The ones I know are uniqueness typing (for example Clean) and effects
tracking (for example D, DDC, upcoming Scala effects system). So, either
you track effects using the type system, or you build a separate system
for it.

/Jesper Nordenberg

martin odersky

unread,
Oct 10, 2011, 2:58:43 AM10/10/11
to Jesper Nordenberg, Ittay Dror, scala-debate
It's certainly straightforward conceptually, but it means that every
program fragment has to be rewritten completely once the first effect
is introduced, even if that effect is just a print statement. I don't
find that very easy to use, and would like to shoot for something more
polymorphic.

Cheers

-- Martin

martin odersky

unread,
Oct 10, 2011, 3:09:26 AM10/10/11
to Ittay Dror, scala-debate
The IO monad does not make a function pure. It just makes it obvious
that it's impure.
An explicit world argument would make the function technically pure,
but again, doing so
is dubious in practice.

If I'd search for good examples where purity is useful, I'd look
elsewhere. For instance, parallel collections allow automatic speedups
in exchange for all computations being pure.

Cheers

-- Martin

Ittay Dror

unread,
Oct 10, 2011, 3:11:57 AM10/10/11
to Jesper Nordenberg, scala-debate



Jesper Nordenberg wrote:
Ittay Dror skrev 2011-10-10 07:16:
The title is probably too much of a troll. Sorry about that.

My questioning is specifically about making functions pure. And the
hoops one need to jump through to get that (at least the way I know
which is the IO monad). More specifically:
1. what does it buy me? in theory, pure functions are nice since they
behave better, but i want to find a use case that appeals to everyday
java developers. That is, a case where making a function pure removes
pitfalls (while the function remains basically the same)

That's basically the same question as "what does a type system buy me?". It enables automatic detection of programming errors at compile time rather than at runtime.

I'd love to see a code example where the introduction of IO[_] makes more errors detectable. In the code example I used in the first message, I couldn't find where it helps.



It also opens up opportunities for performance optimizations and makes your code a whole lot easier to test.

Performance how? Because of laziness? This can also work against you. In the lowest level it looks to me like working through a bind function is less efficient than working with the value directly.

How is code easier to test? I initially thought that when I have a pure function, it will behave the same every time. So once I test it I'm sure it works the same in production. But then I thought: Ok, lets assume that I have a function that closes a socket. Further, assume that closing it a second time creates an error. If I have code that does 'for (_ <- close(socket); _ <- close(socket))', it will fail just the same as the (simpler) imperative code "socket.close; socket.close", wouldn't it? Side effects are not gone. They are just abstracted over, the question is whether the abstraction helps.

Again, would love some code examples.

Adriaan Moors

unread,
Oct 10, 2011, 3:43:04 AM10/10/11
to Meredith Gregory, scala-debate


On Mon, Oct 10, 2011 at 2:47 AM, Meredith Gregory <lgreg.m...@gmail.com> wrote:
It's intriguing to contemplate the possibility of something lighter weight than a three method api!
(I know you're aware of all this, Greg, just pointing it out for the sake of the "debate" part of this mailing list)

so, let's see...

something with few methods may sound like it's lightweight, but that's like saying programming straight in binary is even lighterweight: look! there's only ones and zeroes!

then again, don't forget about the monad laws, which, implicit in your program though they may be, are an important constraint on the interface (2 downsides: they are implicit, they add more constraints)

the real deal breaker, I'd say, is that monads don't compose well -- even simply mixing Option (I mean, Maybe) and List in a for (pardon me, do) comprehension is cumbersome! (not so much in Scala, thanks to CanBuildFrom and breakOut). For a recent proposal to solve monad composition, see "the monad zipper" (http://lambda-the-ultimate.org/node/3929), 

I say, monads are starting to sound like a regular straightjacket by now (well, this is a feature, not a bug -- to some degree)

Personally, I think we (as in, a strict language with subtyping, objects,...) are much better off with a (lightweight...) polymorphic effect system (being developed for Scala by Lukas Rytz -- see also Disciple (http://disciple.ouroborus.net/) for something more Haskelly)


cheers
adriaan

Lars Hupel

unread,
Oct 10, 2011, 4:04:29 AM10/10/11
to scala-...@googlegroups.com
> The IO monad does not make a function pure. It just makes it obvious
> that it's impure.

I have to disagree here, or at least ask why you think so. Let's
consider the function `putStrLn` from Haskell with the type `String ->
IO ()`. The definition of "purity" I am used to basically requires that
`putStrLn "foo"` does not execute a side-effect (true) and does not
depend on (hidden) state such that subsequent executions yield exactly
the same value (also true). The side-effect is executed by the
underlying system only if it is somehow included in `main`.

Kevin Wright

unread,
Oct 10, 2011, 4:31:55 AM10/10/11
to adriaa...@epfl.ch, Meredith Gregory, scala-debate
On 10 October 2011 08:43, Adriaan Moors <adriaa...@epfl.ch> wrote:


On Mon, Oct 10, 2011 at 2:47 AM, Meredith Gregory <lgreg.m...@gmail.com> wrote:
It's intriguing to contemplate the possibility of something lighter weight than a three method api!
(I know you're aware of all this, Greg, just pointing it out for the sake of the "debate" part of this mailing list)

so, let's see...

something with few methods may sound like it's lightweight, but that's like saying programming straight in binary is even lighterweight: look! there's only ones and zeroes!

then again, don't forget about the monad laws, which, implicit in your program though they may be, are an important constraint on the interface (2 downsides: they are implicit, they add more constraints)

the real deal breaker, I'd say, is that monads don't compose well -- even simply mixing Option (I mean, Maybe) and List in a for (pardon me, do) comprehension is cumbersome! (not so much in Scala, thanks to CanBuildFrom and breakOut). For a recent proposal to solve monad composition, see "the monad zipper" (http://lambda-the-ultimate.org/node/3929), 


In the interests of fairness, it's worth pointing out that Option doesn't play especially nicely alongside collection types when used in a comprehension.  My code is littered with .toSeq calls whenever I'm writing code like this:

    for {
      item <- possibleItem.toSeq
      entry <- item.subEntries
    } yield ...

In the same vein, Maybe is also restricted by the need to explicitly project it when using in a comprehension.  As a certain Mr Morris would doubtless be the first to point out, Maybes would be a lot friendlier if map and flatMap were to take the right projection by default on an otherwise unprojected instance.

Not that any of this is a serious problem.  In the case of Options, both myself and Paul took a look at possible solutions, and basically came to the conclusion that it can only be dealt with by using typeclasses after the fashion of scalaz, or by integrating Option more strongly into the collections hierarchy (thus breaking binary compatibility) .  I have high hopes for the next major release of scala :)
 

I say, monads are starting to sound like a regular straightjacket by now (well, this is a feature, not a bug -- to some degree)

Personally, I think we (as in, a strict language with subtyping, objects,...) are much better off with a (lightweight...) polymorphic effect system (being developed for Scala by Lukas Rytz -- see also Disciple (http://disciple.ouroborus.net/) for something more Haskelly)


cheers
adriaan



--
Kevin Wright
mail: kevin....@scalatechnology.com
gtalk / msn : kev.lee...@gmail.com
vibe / skype: kev.lee.wright
steam: kev_lee_wright

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

martin odersky

unread,
Oct 10, 2011, 4:58:15 AM10/10/11
to Lars Hupel, scala-...@googlegroups.com
You can do the same trick for every language: You can say *all* Scala
functions just "assemble" a computation which then gets executed only
by calling main. So instead of Monadic bind, Scala has

val x = a; b

On the type level, every type T would be interpreted as IO[T]. Every
function application is a bind. And voila! No side effects!

So the only thing IO provides is that code *not* using it is certified
to be pure, whereas in Scala everything is in the IO monad.

Cheers

-- Martin

Rex Kerr

unread,
Oct 10, 2011, 5:03:49 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com
On Mon, Oct 10, 2011 at 2:49 AM, Jesper Nordenberg <mega...@yahoo.com> wrote:
Ittay Dror skrev 2011-10-10 07:16:

The title is probably too much of a troll. Sorry about that.

My questioning is specifically about making functions pure. And the
hoops one need to jump through to get that (at least the way I know
which is the IO monad). More specifically:
1. what does it buy me? in theory, pure functions are nice since they
behave better, but i want to find a use case that appeals to everyday
java developers. That is, a case where making a function pure removes
pitfalls (while the function remains basically the same)

That's basically the same question as "what does a type system buy me?". It enables automatic detection of programming errors at compile time rather than at runtime.

It enables automatic detection of _some_ errors; one still would like to know how big of a hassle it is (much more in Java than Scala, to the point of motivating dynamically typed languages!) and how frequent those errors are (not very, when it comes to IO, from what I've seen).
 
It also opens up opportunities for performance optimizations and makes your code a whole lot easier to test.

I could buy the part about testing, but performance is usually better when you pay _less_ attention, not more, to what is allowed and not.  In particular, writing wrappers that are instantiated with real objects in addition to the actual IO you need to do is not a good way to have higher performance.
 
2. what are the alternatives to IO monad? an implicit World argument?

The ones I know are uniqueness typing (for example Clean) and effects tracking (for example D, DDC, upcoming Scala effects system). So, either you track effects using the type system, or you build a separate system for it.

I'm quite skeptical of the value of _any_ of these approaches since the world outside your program does not have type information.  Thus, you end up having to watch for unexpected effects and do run-time identification of your types regardless.  That your type system will catch you if you, say, fail to have a file handle closed at a certain point is usually not  a big deal (and that has lighter-weight solutions anyway for the large majority of cases).

  --Rex

Jesper Nordenberg

unread,
Oct 10, 2011, 5:08:18 AM10/10/11
to scala-...@googlegroups.com
Ittay Dror <ittay.dror <at> gmail.com> writes:
> I'd love to see a code example where the introduction of IO[_] makes
> more errors detectable. In the code example I used in the first
> message, I couldn't find where it helps.

Simple:

foo : Int => Int

If your language is impure (like Scala), foo can format your HD, store and
retrieve stuff in a DB etc. If your language is pure, foo will always return the
same value given the same argument and it will do nothing else, thus it's much
easier to verify it's behavior.

You are thinking backwards, it's not the functions that perform side effects
that are easy to test, it's the ones that have no side effects. Problem in a
language without effects tracking is that you have no idea or control over which
functions perform side effects.

Also, knowing that a function is pure gives the compiler more options for
optimization, it can memoize the return value, it can complete remove calls if
the result is not used, it can parallelize calls etc.

So, how do you keep the language pure and still allow side effects? The IO monad
is a quite natural solution to that.

> If I have
> code that does 'for (_ <- close(socket); _ <- close(socket))',
> it will fail just the same as the (simpler) imperative code
> "socket.close; socket.close", wouldn't it? Side effects are not
> gone. They are just abstracted over, the question is whether the
> abstraction helps.

You have just discovered that ';' is shorthand for flatMap. It's the same
abstraction.

/Jesper Nordenberg


Patrik Andersson

unread,
Oct 10, 2011, 5:16:11 AM10/10/11
to scala-...@googlegroups.com
ok.

foo: Int => IO[Int]

Are you now somehow safe from having your HD reformatted?

√iktor Ҡlang

unread,
Oct 10, 2011, 5:22:35 AM10/10/11
to Patrik Andersson, scala-...@googlegroups.com
On Mon, Oct 10, 2011 at 11:16 AM, Patrik Andersson <pande...@gmail.com> wrote:
ok.

foo: Int => IO[Int]

Are you now somehow safe from having your HD reformatted?


Yes, until you call unsafePerformIO
 



--
Viktor Klang

Akka Tech Lead
Typesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang

Patrik Andersson

unread,
Oct 10, 2011, 5:25:12 AM10/10/11
to √iktor Ҡlang, scala-...@googlegroups.com
So you're not safe then. If calling foo is not optional then neither is having your HD be reformatted. IO buys us nothing in this - admittedly dumb - example.

Ittay Dror

unread,
Oct 10, 2011, 5:29:34 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com

Replies inline. Please note I'm replying from the perspective of an every-day developer, not from the theoretical point of view. I want to find a killer argument for using IO.


Jesper Nordenberg wrote:
Ittay Dror <ittay.dror <at> gmail.com> writes:
    I'd love to see a code example where the introduction of IO[_] makes
    more errors detectable. In the code example I used in the first
    message, I couldn't find where it helps.
Simple:

foo : Int => Int

If your language is impure (like Scala), foo can format your HD, store and 
retrieve stuff in a DB etc. If your language is pure, foo will always return the 
same value given the same argument and it will do nothing else, thus it's much 
easier to verify it's behavior.

This is true in theory, but in practice, I usually know what a function does when I use it.  People don't name their functions 'foo', they name them 'formatHD', 'readFromDB' etc. I can't imagine a situation where I'd write a function named 'factorial' and inside have code to format the HD.

For the same reasons, a function 'printMsg: String => IO[Unit]' can format my HD just as well

If IO[_] is just a tag to mean a function is dangerous, then I'm fine with this approach (it is similar to how Option is an alternative to document the possible return of null), but then why go through the hassle of a monad? I can have:
case class Dangerous[A](value: A)




You are thinking backwards, it's not the functions that perform side effects 
that are easy to test, it's the ones that have no side effects. Problem in a 
language without effects tracking is that you have no idea or control over which 
functions perform side effects.

But when I test I know the function I am testing. It's not a random thing.  Testing is a very big thing in the Java world, people manage to do it fine. Sometime they manage the difference between a test environment and a production environment with DI (so a fetchUser function will work with a real database in production, but be tested with a mock database)



Also, knowing that a function is pure gives the compiler more options for 
optimization, it can memoize the return value, it can complete remove calls if 
the result is not used, it can parallelize calls etc.

True, but this can cause the reverse effect, like large memory consumption, thrashing of the cpu etc. In practice, I'd rather have code that works plainly and then optimize the bottlenecks I find, controlling how I do it. Of course it is easier to optimize pure code (e.g., scala's parallel collections). Btw, when the code is not pure I might need to optimize it manually anyway, so I need to use a profiler anyway.

Jesper Nordenberg

unread,
Oct 10, 2011, 6:29:16 AM10/10/11
to scala-...@googlegroups.com
Patrik Andersson <pandersson <at> gmail.com> writes:
> So you're not safe then. If calling foo is not optional then neither is having
your HD be reformatted. IO buys us nothing in this - admittedly dumb - example.

I don't understand your objection. It's the guaranteed lack of side effects
that buys us something. "Int => IO[Int]" is the same as
"Int => World => (Int, World)" so obviously it's still a pure function.
Compared to "Int => Int" though it's much harder to verify its behaviour.

/Jesper Nordenberg


Lars Hupel

unread,
Oct 10, 2011, 6:35:49 AM10/10/11
to scala-...@googlegroups.com
> This is true in theory, but in practice, I usually know what a function does
> when I use it. People don't name their functions 'foo', they name them
> 'formatHD', 'readFromDB' etc. I can't imagine a situation where I'd write a
> function named 'factorial' and inside have code to format the HD.

Sorry, but that doesn't work. I have stumbled upon so many seemingly non
side-effecting methods with "innocent" names which did some really
fundamental state changes that I came to the conclusion that anything
which is not "documented" in the types harms development.

> For the same reasons, a function 'printMsg: String => IO[Unit]' can format my HD
> just as well

You are mixing two problems. Admittedly, putting so many different side
effects into `IO` is not a good idea. IMHO it would be good to separate
different effects into `Filesystem` or `Network` etc.

> If IO[_] is just a tag to mean a function is dangerous, then I'm fine with this
> approach (it is similar to how Option is an alternative to document the possible
> return of null), but then why go through the hassle of a monad? I can have:
> case class Dangerous[A](value: A)

This smells like you want to re-invent monads, as `Dangerous` can be
treated as a monad. I challenge you to write an API using that class and
I bet that you will use some monadic patterns with or without knowing it.

Jesper Nordenberg

unread,
Oct 10, 2011, 6:47:52 AM10/10/11
to scala-...@googlegroups.com
Ittay Dror <ittay.dror <at> gmail.com> writes:
> This is true in theory, but in practice, I usually know what a
> function does when I use it.  People don't name their functions
> 'foo', they name them 'formatHD', 'readFromDB' etc. I can't imagine
> a situation where I'd write a function named 'factorial' and inside
> have code to format the HD.
> For the same reasons, a function 'printMsg: String => IO[Unit]'
> can format my HD just as well

Fine, you obviously don't need and value compile time checking. Personally,
I value it very high. The IO monad may not be most precise and user friendly
way to encode side effects, but it's a simple solution and compared to having
no effects tracking I would take it any day.

> If IO[_] is just a tag to mean a function is dangerous, then I'm
> fine with this approach (it is similar to how Option is an
> alternative to document the possible return of null), but then why
> go through the hassle of a monad? I can have:
> case class Dangerous[A](value: A)

What's stopping you from doing: def makeSafe[A](d : Dangerous[A]) : A =
d.value? You do realize that many side effects are transitive?

> But when I test I know the function I am testing. It's not a random
> thing.  Testing is a very big thing in the Java world, people manage
> to do it fine. Sometime they manage the difference between a test
> environment and a production environment with DI (so a fetchUser
> function will work with a real database in production, but be tested
> with a mock database)

Really? So you never test an abstract interface method without knowing the
details of the implementation?

That people in the Java world manage to do proper testing is a huge
overstatement. I would rather say that the opposite is true.

> True, but this can cause the reverse effect, like large memory
> consumption, thrashing of the cpu etc. In practice, I'd rather have
> code that works plainly and then optimize the bottlenecks I find,
> controlling how I do it. Of course it is easier to optimize pure
> code (e.g., scala's parallel collections). Btw, when the code is not
> pure I might need to optimize it manually anyway, so I need to use a
> profiler anyway.

So, your argument is that because you can't optimize some parts of the code
you might as well not do it for any part? Or maybe you're totally against
compiler optimizations? The JVM is not the right platform for you then.

/Jesper Nordenberg

Jesper Nordenberg

unread,
Oct 10, 2011, 6:56:34 AM10/10/11
to scala-...@googlegroups.com
martin odersky <martin.odersky <at> epfl.ch> writes:
> So the only thing IO provides is that code *not* using it is certified
> to be pure, whereas in Scala everything is in the IO monad.

That's quite a huge thing, don't you agree?

/Jesper Nordenberg


Ittay Dror

unread,
Oct 10, 2011, 6:59:53 AM10/10/11
to Lars Hupel, scala-...@googlegroups.com



Lars Hupel wrote:
This is true in theory, but in practice, I usually know what a function does 
when I use it. People don't name their functions 'foo', they name them 
'formatHD', 'readFromDB' etc. I can't imagine a situation where I'd write a 
function named 'factorial' and inside have code to format the HD.
Sorry, but that doesn't work. I have stumbled upon so many seemingly non
side-effecting methods with "innocent" names which did some really
fundamental state changes that I came to the conclusion that anything
which is not "documented" in the types harms development.
fair enough, see below.


For the same reasons, a function 'printMsg: String => IO[Unit]' can format my HD 
just as well
You are mixing two problems. Admittedly, putting so many different side
effects into `IO` is not a good idea. IMHO it would be good to separate
different effects into `Filesystem` or `Network` etc.

If IO[_] is just a tag to mean a function is dangerous, then I'm fine with this 
approach (it is similar to how Option is an alternative to document the possible 
return of null), but then why go through the hassle of a monad? I can have:
case class Dangerous[A](value: A)
This smells like you want to re-invent monads, as `Dangerous` can be
treated as a monad. I challenge you to write an API using that class and
I bet that you will use some monadic patterns with or without knowing it.
var bar = foo(3)
println(bar * 2) // doesn't compile, bar is of type Dangerous[Int]
// an ahha moment, realizing foo is dangerous. check what foo actually does and then, after making sure all is well:
println(bar.value * 2)

So I need to jump through a hoop to use foo, but it is a very small one. I don't need to use bind, or propogate Dangerous up the call trace or anything like that.

Ittay Dror

unread,
Oct 10, 2011, 7:08:00 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com



Jesper Nordenberg wrote:
Ittay Dror <ittay.dror <at> gmail.com> writes:
    This is true in theory, but in practice, I usually know what a
    function does when I use it.  People don't name their functions
    'foo', they name them 'formatHD', 'readFromDB' etc. I can't imagine
    a situation where I'd write a function named 'factorial' and inside
    have code to format the HD.
    For the same reasons, a function 'printMsg: String => IO[Unit]'
    can format my HD just as well
Fine, you obviously don't need and value compile time checking. Personally,
I value it very high. The IO monad may not be most precise and user friendly 
way to encode side effects, but it's a simple solution and compared to having 
no effects tracking I would take it any day.

I value it when it brings a lot of benefit for a small effort (which is why I love type inference: it reduces the effort).



    If IO[_] is just a tag to mean a function is dangerous, then I'm
    fine with this approach (it is similar to how Option is an
    alternative to document the possible return of null), but then why
    go through the hassle of a monad? I can have:
    case class Dangerous[A](value: A)
What's stopping you from doing: def makeSafe[A](d : Dangerous[A]) : A = 
d.value? You do realize that many side effects are transitive?
the fact that i'm a decent programmer. otherwise, even in haskell, i can wreck havoc.


    But when I test I know the function I am testing. It's not a random
    thing.  Testing is a very big thing in the Java world, people manage
    to do it fine. Sometime they manage the difference between a test
    environment and a production environment with DI (so a fetchUser
    function will work with a real database in production, but be tested
    with a mock database)
Really? So you never test an abstract interface method without knowing the
details of the implementation?

when i test a function, i know what it is supposed to do (i don't care how). usually, i'm the one who wrote it. It is also usually very obvious if it is pure or not.

as a "pragmatic" example, the command-query segregation principle is a nice one: if a function returns a value it must be pure, if it returns Unit, it means it has side effects. This is of course another way of "tagging".



That people in the Java world manage to do proper testing is a huge
overstatement. I would rather say that the opposite is true.

    True, but this can cause the reverse effect, like large memory
    consumption, thrashing of the cpu etc. In practice, I'd rather have
    code that works plainly and then optimize the bottlenecks I find,
    controlling how I do it. Of course it is easier to optimize pure
    code (e.g., scala's parallel collections). Btw, when the code is not
    pure I might need to optimize it manually anyway, so I need to use a
    profiler anyway.
So, your argument is that because you can't optimize some parts of the code 
you might as well not do it for any part? Or maybe you're totally against
compiler optimizations? The JVM is not the right platform for you then.

It means I rather optimize myself, with the help of a library, than let the compiler do it for me. of course its not black and white here either, small optimizations: sure, huge ones, like memoization and parallelism: no.

Anyways, this is getting into what I call a theological argument, with fuzzy arguments, so lets stop here. If you can show me a piece of code

/Jesper Nordenberg

martin odersky

unread,
Oct 10, 2011, 7:30:40 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com

Sure. But I think there will at some point be better ways to track
effects than monads. Monads are too
rigid. Adding a single println somewhere means you have to rewrite
your whole program.

-- Martin

Tony Morris

unread,
Oct 10, 2011, 7:48:43 AM10/10/11
to martin odersky, Jesper Nordenberg, scala-...@googlegroups.com

This thread is epic.

Jesper Nordenberg

unread,
Oct 10, 2011, 8:24:33 AM10/10/11
to scala-...@googlegroups.com
Tony Morris <tmorris <at> tmorris.net> writes:
> This thread is epic.

Tony, I was just wondering when you would chime in. Care to enlighten us with
your views on the epicness of this thread and the IO monad?

/Jesper Nordenberg


Patrik Andersson

unread,
Oct 10, 2011, 8:34:24 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com
You said that you know what foo: Int => Int does *NOT* do in a laugnage like Haskell. You know that it cannot have side effects. But with foo: Int => Int in a language like Scala you cannot be sure that it does not reformat your HD. With *your* reasoning, that means that foo: Int => IO[Int] could potentially reformat your HD.

My reasoning then is that wrapping side-effecting code in IO or not does not buy protection against serious (side-effecting) bugs. How easy it is to locate such bugs is going to come down to the usual suspects such as: Did the person separate, for example, IO stuff into a single module which can be debugged? If not then it happens all over the place and the whole program runs within the IO monad.

Lars Hupel

unread,
Oct 10, 2011, 9:00:56 AM10/10/11
to scala-...@googlegroups.com
> var bar = foo(3)
> println(bar * 2) // doesn't compile, bar is of type Dangerous[Int]
> // an ahha moment, realizing foo is dangerous. check what foo actually does and
> then, after making sure all is well:
> println(bar.value * 2)
>
> So I need to jump through a hoop to use foo, but it is a very small one. I don't
> need to use bind, or propogate Dangerous up the call trace or anything like that.

Same argument would apply to `Option`. As a careful programmer, you
would always check `x.isDefined` before calling `x.get`, wouldn't you?
Actually, that would be the same as using `null`. Instead, we have that
higher level abstraction called "catamorphism" (or use the pattern
matching instead if you wish) which allows the compiler to check what
you are doing. Same thing for side effects.

Dean Wampler

unread,
Oct 10, 2011, 9:03:26 AM10/10/11
to Jesper Nordenberg, scala-...@googlegroups.com, martin odersky
On Mon, Oct 10, 2011 at 1:49 AM, Jesper Nordenberg <mega...@yahoo.com> wrote:
> Ittay Dror skrev 2011-10-10 07:16:
>>
>> ...

> That's basically the same question as "what does a type system buy me?". It
> enables automatic detection of programming errors at compile time rather
> than at runtime.
>
> It also opens up opportunities for performance optimizations and makes your
> code a whole lot easier to test.
>
> …

Types also document aspects of the program for the reader.

--
Dean Wampler
"Functional Programming for Java Developers" and
"Programming Scala" (O'Reilly)
twitter: @deanwampler, @chicagoscala
http://polyglotprogramming.com

Lars Hupel

unread,
Oct 10, 2011, 9:06:23 AM10/10/11
to scala-...@googlegroups.com
> My reasoning then is that wrapping side-effecting code in IO or not does not
> buy protection against serious (side-effecting) bugs. How easy it is to
> locate such bugs is going to come down to the usual suspects such as: Did
> the person separate, for example, IO stuff into a single modu