This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Re: Re: questioning FP

11 replies
Patrik Andersson
Joined: 2009-11-16,
User offline. Last seen 42 years 45 weeks ago.
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
Joined: 2009-11-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: questioning FP
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.

On Mon, Oct 10, 2011 at 11:22 AM, √iktor Ҡlang <viktor [dot] klang [at] gmail [dot] com> wrote:


On Mon, Oct 10, 2011 at 11:16 AM, Patrik Andersson <pandersson [at] gmail [dot] com> wrote:
ok.
foo: Int => IO[Int]
Are you now somehow safe from having your HD reformatted?

Yes, until you call unsafePerformIO
 

On Mon, Oct 10, 2011 at 11:08 AM, Jesper Nordenberg <megagurka [at] yahoo [dot] com> 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.

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






--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: questioning FP


On Mon, Oct 10, 2011 at 11:16 AM, Patrik Andersson <pandersson [at] gmail [dot] com> wrote:
ok.
foo: Int => IO[Int]
Are you now somehow safe from having your HD reformatted?

Yes, until you call unsafePerformIO
 

On Mon, Oct 10, 2011 at 11:08 AM, Jesper Nordenberg <megagurka [at] yahoo [dot] com> 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.

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






--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Jesper Nordenberg
Joined: 2008-12-27,
User offline. Last seen 42 years 45 weeks ago.
Re: questioning FP

Patrik Andersson 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

Patrik Andersson
Joined: 2009-11-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: questioning FP
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.

On Mon, Oct 10, 2011 at 12:29 PM, Jesper Nordenberg <megagurka [at] yahoo [dot] com> wrote:
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
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
Re: questioning FP

> 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.

The usual approach is to write as many parts of your program as pure
functions and as few functions as possible using IO. That turns out to
work quite well.

Ittay Dror 2
Joined: 2010-05-05,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: questioning FP
body p { margin-bottom: 0cm; margin-top: 0pt; }



Razvan Cojocaru wrote:
E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite"> Gents, isn't the STM monad a better fit for this discussion than the IO? There might be less questions as to it's intinsic value and more focus on the monadic constructs and alternatives.

You mean the ST Monad? Isn't it for just local state threads? To allow for computations to tap into the performance benefits of mutable data?

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Martin, you stated that possibility a few times already - I'm sure we're all really curious now about what you have in mind :)
+1

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Also, since monads don't compose etc, why hasn't the discussion mutate to Applicative?

Because someone has to generate the applicative. Once you have 2 functions that takes normal arguments and produce an Applicative, that applicative needs to be a monad for the functions to compose. AFAIU, monads don't compose in that M[_] and N[_] cannot be composed to M[N[_]], which is why all side effects (reading a file, opening a socket, querying a DB) all use IO and not a more specific monad (like FileIO, SocketAction etc.), if they did, and you needed to use to such functions with different monads you'd need to compose them, but you would not be able to

Ittay

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Thanks, Razvan
On 2011-10-10, at 2:32 PM, "nicolas [dot] oury [at] gmail [dot] com" rel="nofollow">nicolas [dot] oury [at] gmail [dot] com" <nicolas [dot] oury [at] gmail [dot] com" rel="nofollow">nicolas [dot] oury [at] gmail [dot] com> wrote:

On Mon, Oct 10, 2011 at 6:50 PM, Runar Bjarnason <runarorama [at] gmail [dot] com" rel="nofollow">runarorama [at] gmail [dot] com> wrote:
The fact that it's possible to defeat an effect system (for example, in the presence of unsafePerformIO) does not mean that it's not useful. Just like the type system remains useful even though it's possible to defeat that (e.g. in the presence of asInstanceOf).


You can also measure how often you want to circuvemt a type system. | think that the main problem is the coarsity of IO.
Razvan Cojocaru 3
Joined: 2010-07-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: questioning FP
I meant the STM, shared transactional memory (or rather software transactional memory?). 

Thanks,Razvan
On 2011-10-10, at 3:54 PM, Ittay Dror <ittay [dot] dror [at] gmail [dot] com> wrote:



Razvan Cojocaru wrote:
E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite"> Gents, isn't the STM monad a better fit for this discussion than the IO? There might be less questions as to it's intinsic value and more focus on the monadic constructs and alternatives.

You mean the ST Monad? Isn't it for just local state threads? To allow for computations to tap into the performance benefits of mutable data?

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Martin, you stated that possibility a few times already - I'm sure we're all really curious now about what you have in mind :)
+1

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Also, since monads don't compose etc, why hasn't the discussion mutate to Applicative?

Because someone has to generate the applicative. Once you have 2 functions that takes normal arguments and produce an Applicative, that applicative needs to be a monad for the functions to compose. AFAIU, monads don't compose in that M[_] and N[_] cannot be composed to M[N[_]], which is why all side effects (reading a file, opening a socket, querying a DB) all use IO and not a more specific monad (like FileIO, SocketAction etc.), if they did, and you needed to use to such functions with different monads you'd need to compose them, but you would not be able to

Ittay

E40BA012-3599-4DE6-A466-98C538633898 [at] razie [dot] com" type="cite">
Thanks, Razvan
On 2011-10-10, at 2:32 PM, "nicolas [dot] oury [at] gmail [dot] com" rel="nofollow">nicolas [dot] oury [at] gmail [dot] com" <nicolas [dot] oury [at] gmail [dot] com" rel="nofollow">nicolas [dot] oury [at] gmail [dot] com> wrote:

On Mon, Oct 10, 2011 at 6:50 PM, Runar Bjarnason <runarorama [at] gmail [dot] com" rel="nofollow">runarorama [at] gmail [dot] com> wrote:
The fact that it's possible to defeat an effect system (for example, in the presence of unsafePerformIO) does not mean that it's not useful. Just like the type system remains useful even though it's possible to defeat that (e.g. in the presence of asInstanceOf).


You can also measure how often you want to circuvemt a type system. | think that the main problem is the coarsity of IO.
runaro
Joined: 2009-11-13,
User offline. Last seen 32 weeks 2 days ago.
Re: Re: questioning FP


On Monday, October 10, 2011 3:54:05 PM UTC-4, Ittay Dror wrote:
Because someone has to generate the applicative. Once you have 2 functions that takes normal arguments and produce an Applicative, that applicative needs to be a monad for the functions to compose. AFAIU, monads don't compose in that M[_] and N[_] cannot be composed to M[N[_]], which is why all side effects (reading a file, opening a socket, querying a DB) all use IO and not a more specific monad (like FileIO, SocketAction etc.), if they did, and you needed to use to such functions with different monads you'd need to compose them, but you would not be able to


This is not true. It's entirely possible for you to have a more granular DSL for your IO actions than simply the opaque "IO". And they can be monads individually, and they can commute with each other easily. For example, look at the MonadIO type class from the Haskell libraries. It's specifically for this purpose, to allow "io-like" monads to commute.

Don't take it to mean more than it does when people say that "monads don't compose". What that really means is this: If M[_] is a monad, and N[_] is a monad, then M[N[_]] may not be a monad. But it often is, and it depends on what M and N are. The implementation of the M[N[_]] monad then depends on the nature of one or the other monad, either M or N.

Applicative functors, on the other hand, always compose. If M[_] is Applicative, and N[_] is Applicative, then M[N[_]] is Applicative too, in a completely mechanical way that in no way depends on what M and N are. Note that all monads are applicative functors, but not all applicative functors are monads. So you can say that all monads M and N compose to form an applicative functor M[N[_]].

What is the difference between applicative functors and monads then? It's very simple. If you have two "effectful" functions, f:A=>M[B], and g:B=>M[C], then if you compose them, the "effect" produced by g depends on the value returned by f. So M has to be a monad if you want to compose them. But if you have two "effectful" values a:M[A] and b:M[B], then you can compose them with a "pure" function p:(A,B)=>C to form a result of type M[C]. In that case you only need an applicative functor because there is no dependency between the effects.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: questioning FP

On Tue, Oct 11, 2011 at 11:45, Runar Bjarnason wrote:
>
> Don't take it to mean more than it does when people say that "monads don't
> compose". What that really means is this: If M[_] is a monad, and N[_] is a
> monad, then M[N[_]] may not be a monad. But it often is, and it depends on
> what M and N are. The implementation of the M[N[_]] monad then depends on
> the nature of one or the other monad, either M or N.
>
> Applicative functors, on the other hand, always compose. If M[_] is
> Applicative, and N[_] is Applicative, then M[N[_]] is Applicative too, in a
> completely mechanical way that in no way depends on what M and N are. Note
> that all monads are applicative functors, but not all applicative functors
> are monads. So you can say that all monads M and N compose to form an
> applicative functor M[N[_]].
>
> What is the difference between applicative functors and monads then? It's
> very simple. If you have two "effectful" functions, f:A=>M[B], and
> g:B=>M[C], then if you compose them, the "effect" produced by g depends on
> the value returned by f. So M has to be a monad if you want to compose them.
> But if you have two "effectful" values a:M[A] and b:M[B], then you can
> compose them with a "pure" function p:(A,B)=>C to form a result of type
> M[C]. In that case you only need an applicative functor because there is no
> dependency between the effects.

Fantastic explanation. If this is the kind of explanation that book is
going to come with, then it's going to be one of the best book
acquisitions I ever made.

You should blog these three paragraphs, with, perhaps, *very* brief
examples. What make them so good is precisely their concision.

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Fwd: Re: questioning FP

---------- Forwarded message ----------
From: Rex Kerr
Date: Sat, Oct 15, 2011 at 2:40 PM
Subject: Re: [scala-debate] Re: questioning FP
To: Sébastien Bocq
Cc: Scala Users

On Sat, Oct 15, 2011 at 2:02 PM, Sébastien Bocq
wrote:
>
> True, but is there a good practical use case for maintaining extremely large stacks that must be traversed one element at a time in one sweep?
>
> You can build ropes on top finger trees, which present a good trade-off between traversing and other manipulation functions. But this is another discussion.
>
> Could you give an example of the flat structures common for IO? I don't see what you mean.

Sometimes it doesn't matter whether it's a stack or queue or what, as
long as you have all the elements.  And anyway, _either_ use case is
an argument for a list, you don't need both (i.e. either a stack, or
mostly traversals).

Flat structures for IO?  Okay.  From the Java API:

Class FileOutputStream

void write(byte[] b, int off, int len)
          Writes len bytes from the specified byte array starting at
offset off to this file output stream.

byte[] b looks pretty flat to me.

Any time you have a text file (and you want to read it in quickly so
other processes can modify it) you end up with a flat pile of lines of
text (or bytes).  Any time you spend unflattening and parsing it in
your IO thread is wasted time when IO rates are limiting.  (On
machines with many cores, this is almost always.)

A HTTP get request gives you a flat string.

And so on.

  --Rex

Sebastien Bocq
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: questioning FP

2011/10/15 Rex Kerr <ichoran [at] gmail [dot] com>


On Sat, Oct 15, 2011 at 2:02 PM, Sébastien Bocq <sebastien [dot] bocq [at] gmail [dot] com> wrote:
True, but is there a good practical use case for maintaining extremely large stacks that must be traversed one element at a time in one sweep?

You can build ropes on top finger trees, which present a good trade-off between traversing and other manipulation functions. But this is another discussion.

Could you give an example of the flat structures common for IO? I don't see what you mean.

Sometimes it doesn't matter whether it's a stack or queue or what, as long as you have all the elements.  And anyway, _either_ use case is an argument for a list, you don't need both (i.e. either a stack, or mostly traversals).

Flat structures for IO?  Okay.  From the Java API:

Class FileOutputStream

void write(byte[] b, int off, int len)
          Writes len bytes from the specified byte array starting at offset off to this file output stream.

byte[] b looks pretty flat to me.


Ok. They don't need to be large.
 
Any time you have a text file (and you want to read it in quickly so other processes can modify it) you end up with a flat pile of lines of text (or bytes). 

Stored in blocks of 4KB. If you can, it is more efficient to process these blocks as they arrive (e.g. UNIX pipes) than reading megabytes of texts into memory before doing anything.
 
Any time you spend unflattening and parsing it in your IO thread is wasted time when IO rates are limiting.  (On machines with many cores, this is almost always.)

A HTTP get request gives you a flat string.

A ByteString [1] or a rope [2] would be better.

[1] Rewriting Haskell Strings
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.9065&rep=rep1&type=pdf

[2] "Ropes: an Alternative to Strings"
http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf

It is not because API's are build this way today that they can't be made better in the future. A problem today is that these structures are impractical to implement or use when all you have is first-order functions. Back on track!
 
And so on.

  --Rex


--
Sébastien

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland