Martin Odersky on "What's wrong with Monads"

classic Classic list List threaded Threaded
34 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Patrick Hurst-2
I'd also say that reading command-line flags inside a simple function like amount is a pretty large code smell. The only case in which it isn't would be when the codebase is so small that redesigning the Haskell to be in IO (or switch between amountPlus and amountTimes) is negligible anyway.

On Jun 26, 2012, at 18:59, Ozgun Ataman <[hidden email]> wrote:

We could debate this endlessly (as is common), but I would argue that a "clean" design would make the option and alternative of multiplying explicit in its design instead of including calls to fetch command line arguments in an ad-hoc fashion everywhere. 

The Haskell way of encoding this would be to define an app configuration data type (say AppConfig), parse the command line arguments into it upfront in IO and then run your application either in a in a monad that's an instance of (MonadReader MyConfig) or explicitly pass the option in where needed by a function. If you've designed your application this way, adding a new command line option would cause very little -if any- refactoring. If not, in my experience it is usually a 30 minute intense refactoring campaign.

I suspect there might be a way to use implicit arguments here as well, but that's something I've never felt compelled to use.

This kind of separation of concerns and "pure" application design is one of the things that (I think) many people really like about Haskell.

Cheers,
Oz

On Tuesday, June 26, 2012 at 6:19 PM, Tillmann Rendel wrote:

Hi,

MightyByte wrote:
Of course every line of your program that uses a Foo will change if you switch
to IO Foo instead.

But we often have to also change lines that don't use Foo at all. For
example, here is the type of binary trees of integers:

data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)

A function to add up all integers in a tree:

amount:: Tree -> Integer
amount (Leaf x) = x
amount (Branch t1 t2) = amountt1 + amountt2

All fine so far. Now, consider the following additional requirement: "If
the command-line flag --multiply is set, the function amount computes
the product instead of the sum."

In a language with implicit side effects, it is easy to implement this.
We just change the third line of the amount function to check whether to
call (+) or (*). In particular, we would not touch the other two lines.

How would you implement this requirement in Haskell without changing the
line "amount (Leaf x) = x"?

(I actually see three ways of doing this in Haskell, but all have
serious drawbacks and do not fully solve the problem).

Here it seems not so bad just to change all three lines of the amount
function, even if they are not strictly related to the semantic change
we want to make. But in a real program, this situation can translate to
changing thousands of lines of code in many functions just to implement
a minor change to a single requirement.

Tillmann

_______________________________________________
Haskell-Cafe mailing list

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Nathan Howell-2
In reply to this post by Tillmann Rendel-5
On Tue, Jun 26, 2012 at 3:19 PM, Tillmann Rendel
<[hidden email]> wrote:

> A function to add up all integers in a tree:
>
>  amount:: Tree -> Integer
>  amount (Leaf x) = x
>  amount (Branch t1 t2) = amountt1 + amountt2
>
> All fine so far. Now, consider the following additional requirement: "If the
> command-line flag --multiply is set, the function amount computes the
> product instead of the sum."
>
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

One option is to encode the desired behavior at the type level. By
extended the data type slightly and adding a Functor instance,
selecting between a product and a sum can be done using their Monoid
newtypes:

import Data.Monoid
import System.Environment

data Tree a = Leaf a | Branch (Tree a) (Tree a)

instance Functor Tree where
  f `fmap` Leaf x = Leaf (f x)
  f `fmap` Branch x y = Branch (fmap f x) (fmap f y)

amount :: Monoid a => Tree a -> a
amount (Leaf x) = x
amount (Branch t1 t2) = amount t1 <> amount t2

main :: IO ()
main = do
  args <- getArgs

  let val :: Tree Int
      val = Branch (Leaf 8) (Leaf 18)

  let getResult :: Tree Int -> Int
      getResult = case args of
        ["--multiply"] -> getProduct . amount . fmap Product
        _              -> getSum     . amount . fmap Sum

  print . getResult $ val

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Tillmann Rendel-5
In reply to this post by Tillmann Rendel-5
Hi Rico,

Rico Moorman wrote:

>>   data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)
>>
>>   amount:: Tree -> Integer
>>   amount (Leaf x) = x
>>   amount (Branch t1 t2) = amountt1 + amountt2
>>
>> [...] additional requirement: "If the command-line flag --multiply is set,
>> the function amount computes the product instead of the sum."
>>
>> How would you implement this requirement in Haskell without changing the
>> line "amount (Leaf x) = x"?

> The (for me at least) most obvious way to do this would be, to make the
> operation to be applied to determine the amount (+ or *) an explicit
> parameter in the function's definition.
>
>   data Tree a = Leaf a
>               | Branch (Tree a) (Tree a)
>   amount :: (a -> a -> a) -> Tree a -> a
>   amount fun (Leaf x) = x
>   amount fun (Branch t1 t2) = amount fun t1 `fun` amount fun t2

I agree: This is the most obvious way, and also a very good way. I would
probably do it like this.

> Which drawbacks do you see besides increased verbosity?

Well, you did change the equation "amount (Leaf x) = x" to "amount fun
(Leaf x) = x". In a larger example, this means that you need to change
many lines of many functions, just to get the the value of fun from the
point where it is known to the point where you need it.

> [...] I am wondering which ways of doing this in Haskell you mean.

I thought of the following three options, but see also Nathan Howells
email for another alternative (that is related to my option (1) below):


(1) Implicit parameters:

{-# LANGUAGE ImplicitParams #-}
data Tree = Leaf Integer | Branch Tree Tree

amount :: (?fun :: Integer -> Integer -> Integer) => Tree -> Integer
amount (Leaf x) = x
amount (Branch t1 t2) = ?fun (amount t1) (amount t2)


(2) Lexical Scoping:

data Tree = Leaf Integer | Branch Tree Tree

amount :: (Integer -> Integer -> Integer) -> Tree -> Integer
amount fun = amount where {
amount (Leaf x) = x
; amount (Branch t1 t2) = fun (amount t1) (amount t2) }


(3) UnsafePerformIO:

import System.IO.Unsafe (unsafePerformIO)

data Tree = Leaf Integer | Branch Tree Tree

amount :: Tree -> Integer
amount (Leaf x) = x
amount (Branch t1 t2) = fun (amount t1) (amount t2)
   where fun = unsafePerformIO ...


I'm not happy with any of these options. Personally, I would probably go
ahead and transform the whole program just to get the value of fun to
where it is needed. Nevertheless, having actually done this before, I
understand why Martin Odersky doesn't like doing it :)

   Tillmann

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Yves Parès-3
> I'm not happy with any of these options.

Why are you unhappy with the ImplicitParams option?

It's pretty much like resorting to a newtype, as it's been suggested before.

2012/6/27 Tillmann Rendel <[hidden email]>
Hi Rico,

Rico Moorman wrote:
 data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)

 amount:: Tree -> Integer
 amount (Leaf x) = x
 amount (Branch t1 t2) = amountt1 + amountt2

[...] additional requirement: "If the command-line flag --multiply is set,

the function amount computes the product instead of the sum."

How would you implement this requirement in Haskell without changing the
line "amount (Leaf x) = x"?

The (for me at least) most obvious way to do this would be, to make the
operation to be applied to determine the amount (+ or *) an explicit
parameter in the function's definition.


 data Tree a = Leaf a
             | Branch (Tree a) (Tree a)
 amount :: (a -> a -> a) -> Tree a -> a
 amount fun (Leaf x) = x
 amount fun (Branch t1 t2) = amount fun t1 `fun` amount fun t2

I agree: This is the most obvious way, and also a very good way. I would probably do it like this.

Which drawbacks do you see besides increased verbosity?

Well, you did change the equation "amount (Leaf x) = x" to "amount fun (Leaf x) = x". In a larger example, this means that you need to change many lines of many functions, just to get the the value of fun from the point where it is known to the point where you need it.

[...] I am wondering which ways of doing this in Haskell you mean.

I thought of the following three options, but see also Nathan Howells email for another alternative (that is related to my option (1) below):


(1) Implicit parameters:

{-# LANGUAGE ImplicitParams #-}
data Tree = Leaf Integer | Branch Tree Tree

amount :: (?fun :: Integer -> Integer -> Integer) => Tree -> Integer

amount (Leaf x) = x
amount (Branch t1 t2) = ?fun (amount t1) (amount t2)


(2) Lexical Scoping:

data Tree = Leaf Integer | Branch Tree Tree

amount :: (Integer -> Integer -> Integer) -> Tree -> Integer
amount fun = amount where {

amount (Leaf x) = x
; amount (Branch t1 t2) = fun (amount t1) (amount t2) }


(3) UnsafePerformIO:

import System.IO.Unsafe (unsafePerformIO)

data Tree = Leaf Integer | Branch Tree Tree


amount :: Tree -> Integer
amount (Leaf x) = x
amount (Branch t1 t2) = fun (amount t1) (amount t2)
 where fun = unsafePerformIO ...


I'm not happy with any of these options. Personally, I would probably go ahead and transform the whole program just to get the value of fun to where it is needed. Nevertheless, having actually done this before, I understand why Martin Odersky doesn't like doing it :)


 Tillmann

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Alberto G. Corona
The "problem" of monads is that it defines different execution models,  besides the funcional,/lazy/declarative mode. There is no such problem in imperative languages, which work ever in an hardwired IO monad. But this means that the programmer has to code  the extra behaviour needed in each application  to do the same.


It pretend to be intuitive, not accurate. (See disclaimer ;)  . Comments welcome

2012/6/27 Yves Parès <[hidden email]>
> I'm not happy with any of these options.

Why are you unhappy with the ImplicitParams option?

It's pretty much like resorting to a newtype, as it's been suggested before.


2012/6/27 Tillmann Rendel <[hidden email]>
Hi Rico,

Rico Moorman wrote:
 data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)

 amount:: Tree -> Integer
 amount (Leaf x) = x
 amount (Branch t1 t2) = amountt1 + amountt2

[...] additional requirement: "If the command-line flag --multiply is set,

the function amount computes the product instead of the sum."

How would you implement this requirement in Haskell without changing the
line "amount (Leaf x) = x"?

The (for me at least) most obvious way to do this would be, to make the
operation to be applied to determine the amount (+ or *) an explicit
parameter in the function's definition.


 data Tree a = Leaf a
             | Branch (Tree a) (Tree a)
 amount :: (a -> a -> a) -> Tree a -> a
 amount fun (Leaf x) = x
 amount fun (Branch t1 t2) = amount fun t1 `fun` amount fun t2

I agree: This is the most obvious way, and also a very good way. I would probably do it like this.

Which drawbacks do you see besides increased verbosity?

Well, you did change the equation "amount (Leaf x) = x" to "amount fun (Leaf x) = x". In a larger example, this means that you need to change many lines of many functions, just to get the the value of fun from the point where it is known to the point where you need it.

[...] I am wondering which ways of doing this in Haskell you mean.

I thought of the following three options, but see also Nathan Howells email for another alternative (that is related to my option (1) below):


(1) Implicit parameters:

{-# LANGUAGE ImplicitParams #-}
data Tree = Leaf Integer | Branch Tree Tree

amount :: (?fun :: Integer -> Integer -> Integer) => Tree -> Integer

amount (Leaf x) = x
amount (Branch t1 t2) = ?fun (amount t1) (amount t2)


(2) Lexical Scoping:

data Tree = Leaf Integer | Branch Tree Tree

amount :: (Integer -> Integer -> Integer) -> Tree -> Integer
amount fun = amount where {

amount (Leaf x) = x
; amount (Branch t1 t2) = fun (amount t1) (amount t2) }


(3) UnsafePerformIO:

import System.IO.Unsafe (unsafePerformIO)

data Tree = Leaf Integer | Branch Tree Tree


amount :: Tree -> Integer
amount (Leaf x) = x
amount (Branch t1 t2) = fun (amount t1) (amount t2)
 where fun = unsafePerformIO ...


I'm not happy with any of these options. Personally, I would probably go ahead and transform the whole program just to get the value of fun to where it is needed. Nevertheless, having actually done this before, I understand why Martin Odersky doesn't like doing it :)


 Tillmann

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe



_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

MightyByte
In reply to this post by Tillmann Rendel-5
Tillmann Rendel <rendel <at> informatik.uni-marburg.de> writes:

>
> > Which drawbacks do you see besides increased verbosity?
>
> Well, you did change the equation "amount (Leaf x) = x" to "amount fun
> (Leaf x) = x". In a larger example, this means that you need to change
> many lines of many functions, just to get the the value of fun from the
> point where it is known to the point where you need it.

I would argue that no matter how good one's language is, there will always exist
realistic refactorings that require you to make sweeping changes to a large
portion of your code base.  

> > [...] I am wondering which ways of doing this in Haskell you mean.
>
> I thought of the following three options, but see also Nathan Howells
> email for another alternative (that is related to my option (1) below):
>
> (1) Implicit parameters:
<snip>
> (2) Lexical Scoping:
<snip>
> (3) UnsafePerformIO:
<snip>
> I'm not happy with any of these options. Personally, I would probably go
> ahead and transform the whole program just to get the value of fun to
> where it is needed. Nevertheless, having actually done this before, I
> understand why Martin Odersky doesn't like doing it :)

I think that Martin's statement is an unavoidable fact of life that follows
directly from the definition of purity and the mechanics of data dependencies.  
Of course nobody likes making sweeping changes to their app because they didn't
anticipate the way the future would evolve.  But lets not blame monads for what
is really a much more fundamental phenomenon.





_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Fwd: Martin Odersky on "What's wrong with Monads"

M Farkas-Dyck
In reply to this post by Nathan Howell-2
On 26/06/2012, Nathan Howell <[hidden email]> wrote:

> On Tue, Jun 26, 2012 at 3:19 PM, Tillmann Rendel
> <[hidden email]> wrote:
>> All fine so far. Now, consider the following additional requirement: "If
>> the
>> command-line flag --multiply is set, the function amount computes the
>> product instead of the sum."
>>
>> How would you implement this requirement in Haskell without changing the
>> line "amount (Leaf x) = x"?
>
> One option is to encode the desired behavior at the type level. By
> extended the data type slightly and adding a Functor instance,
> selecting between a product and a sum can be done using their Monoid
> newtypes: ...

Better yet, use foldMap:

> import Data.Monoid
> import Data.Foldable
> import System.Environment

> data Tree a = Leaf a | Branch (Tree a) (Tree a)

> instance Functor Tree where
>   f `fmap` Leaf x = Leaf (f x)
>   f `fmap` Branch x y = Branch (fmap f x) (fmap f y)

> instance Foldable Tree where
>   foldMap f (Leaf x) = f x
>   foldMap f (Branch s t) = foldMap f s <> foldMap f t

> main :: IO ()
> main = do
>   args <- getArgs

>   let val :: Tree Int
>       val = Branch (Leaf 8) (Leaf 18)

>   let getResult :: Tree Int -> Int
>       getResult = case args of
>         ["--multiply"] -> getProduct . foldMap Product
>         _              -> getSum     . foldMap Sum

>   print . getResult $ val

Yet better yet:

> {-# LANGUAGE DeriveFunctor, DeriveFoldable #-}

> import Data.Monoid;
> import Data.Foldable;
> import System.Environment

> data Tree a = Leaf a | Branch (Tree a) (Tree a)
>   deriving (Functor, Foldable);

> ...

(^_^)

--
Strake

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Donn Cave-4
In reply to this post by Jonathan Geddes
To be fair, while it's great that Functor et al. are available to
help solve problems like that with a Tree data structure, in the
present context it mostly shows that a trivial example was used
to illustrate the problem.  Unless I'm missing how this generic
fold functionality solves the more general problem.

Quoth Alberto G. Corona,

> ... There is no such problem in imperative languages, which work
> ever in an hardwired IO monad.

Or to look at it from the other direction, the problem with Haskell
is that it allows the programmer to write non-IO functions.  To the
extent that's a real problem, the solution would obviously be to
exercise some restraint with the pure code and write more in the
IO monad.  If you will humor me, we understand that this code is
equally satisfactory from a mathematical perspective and there's
no crime in it.  But does it help with the `flags problem?'

Suppose that functions f and g both have IO types;  f calls g,
and g has optional behavior depending on some IO value computed
previously, the flag.  The way I understand it, I can accomplish
this without passing the flag around:  with an IORef.

What's less clear to me is whether Haskell really supports the module
level accessible IORef value I need.  Also known as "top level mutable
state."  Is the NOINLINE pragma I've been using with GHC, really a
sound and reliable practice that we're reasonably confident will be
supported by other compilers?

It feels to me like I'm pulling a weird trick, to do something that's
trivial in other programming languages.  Maybe that's part of the
problem, that imperative programming receives something a little short
of full support in Haskell, inasmuch as basic features like top level
mutable state require relatively arcane incantations.

        Donn

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Dominique Devriese-2
In reply to this post by Tillmann Rendel-5
2012/6/27 Tillmann Rendel <[hidden email]>:

> MightyByte wrote:
>>
>> Of course every line of your program that uses a Foo will change if you
>> switch
>> to IO Foo instead.
>
>
> But we often have to also change lines that don't use Foo at all. For
> example, here is the type of binary trees of integers:
>
>  data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)
>
> A function to add up all integers in a tree:
>
>  amount:: Tree -> Integer
>  amount (Leaf x) = x
>  amount (Branch t1 t2) = amountt1 + amountt2
>
> All fine so far. Now, consider the following additional requirement: "If the
> command-line flag --multiply is set, the function amount computes the
> product instead of the sum."
>
> In a language with implicit side effects, it is easy to implement this. We
> just change the third line of the amount function to check whether to call
> (+) or (*). In particular, we would not touch the other two lines.
>
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

I may be missing the point here, but having worked on large code bases
with a wide variety contributors before, I find it very advantageous
that programmers are prevented from writing an amount function whose
behaviour depends on command line arguments without at least an
indication in the type. The fact that the function can not perform
stuff like that is precisely the guarantee that the Haskell type gives
me...

Dominique

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

damodar kulkarni
Any idea whether Martin Odersky has read this discussion?

Thanks and regards,
-Damodar Kulkarni


On Fri, Jun 29, 2012 at 12:23 AM, Dominique Devriese <[hidden email]> wrote:
2012/6/27 Tillmann Rendel <[hidden email]>:
> MightyByte wrote:
>>
>> Of course every line of your program that uses a Foo will change if you
>> switch
>> to IO Foo instead.
>
>
> But we often have to also change lines that don't use Foo at all. For
> example, here is the type of binary trees of integers:
>
>  data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)
>
> A function to add up all integers in a tree:
>
>  amount:: Tree -> Integer
>  amount (Leaf x) = x
>  amount (Branch t1 t2) = amountt1 + amountt2
>
> All fine so far. Now, consider the following additional requirement: "If the
> command-line flag --multiply is set, the function amount computes the
> product instead of the sum."
>
> In a language with implicit side effects, it is easy to implement this. We
> just change the third line of the amount function to check whether to call
> (+) or (*). In particular, we would not touch the other two lines.
>
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

I may be missing the point here, but having worked on large code bases
with a wide variety contributors before, I find it very advantageous
that programmers are prevented from writing an amount function whose
behaviour depends on command line arguments without at least an
indication in the type. The fact that the function can not perform
stuff like that is precisely the guarantee that the Haskell type gives
me...

Dominique

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe





_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Radical
In reply to this post by Tillmann Rendel-5
On Tue, Jun 26, 2012 at 6:19 PM, Tillmann Rendel
<[hidden email]> wrote:
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

The hflags library [http://hackage.haskell.org/package/hflags] seems
to do that, however...

> (I actually see three ways of doing this in Haskell, but all have serious
> drawbacks and do not fully solve the problem).

...it uses the unsafe IO trick
[http://www.haskell.org/haskellwiki/Top_level_mutable_state], which
may be one of those three ways you aren't fond of.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Radical
In reply to this post by Dominique Devriese-2
On Thu, Jun 28, 2012 at 2:53 PM, Dominique Devriese
<[hidden email]> wrote:

> 2012/6/27 Tillmann Rendel <[hidden email]>:
>> How would you implement this requirement in Haskell without changing the
>> line "amount (Leaf x) = x"?
>
> I may be missing the point here, but having worked on large code bases
> with a wide variety contributors before, I find it very advantageous
> that programmers are prevented from writing an amount function whose
> behaviour depends on command line arguments without at least an
> indication in the type. The fact that the function can not perform
> stuff like that is precisely the guarantee that the Haskell type gives
> me...

I don't think there's an answer that's uniformly right; it depends on
whether you think of the input to the program, e.g. the environment,
command-line arguments, etc. as 'constant' and in some sense, pure.
The latter are constant in the sense that they never change, but they
are not fixed at compile-time. Other languages effectively treat them
as pure (by passing them directly to main), whereas Haskell chooses
not to, which is probably the reason why getArgs has IO in its type
(something that seems unintuitive at first.)

That precedent supports the view that e.g. a command-line flag
shouldn't affect behavior without the type reflecting it, e.g. by
doing IO, but the de facto use of the unsafe IO trick means not
everyone agrees.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

Facundo Domínguez
In reply to this post by Jonathan Geddes
> That precedent supports the view that e.g. a command-line flag
> shouldn't affect behavior without the type reflecting it, e.g. by
> doing IO, but the de facto use of the unsafe IO trick means not
> everyone agrees.

For those interested, here's [1] a case where treating command line
arguments as top level constants went wrong.
Look specifically at the section named "Crime Doesn't Pay".

Best,
Facundo

[1] http://www.aosabook.org/en/ghc.html


> Date: Tue, 3 Jul 2012 17:49:48 -0400
> From: Alvaro Gutierrez <[hidden email]>
> Subject: Re: [Haskell-cafe] Martin Odersky on "What's wrong with
>         Monads"
> To: Dominique Devriese <[hidden email]>
> Cc: [hidden email]
> Message-ID:
>         <[hidden email]>
> Content-Type: text/plain; charset=ISO-8859-1
>
> On Thu, Jun 28, 2012 at 2:53 PM, Dominique Devriese
> <[hidden email]> wrote:
>> 2012/6/27 Tillmann Rendel <[hidden email]>:
>>> How would you implement this requirement in Haskell without changing the
>>> line "amount (Leaf x) = x"?
>>
>> I may be missing the point here, but having worked on large code bases
>> with a wide variety contributors before, I find it very advantageous
>> that programmers are prevented from writing an amount function whose
>> behaviour depends on command line arguments without at least an
>> indication in the type. The fact that the function can not perform
>> stuff like that is precisely the guarantee that the Haskell type gives
>> me...
>
> I don't think there's an answer that's uniformly right; it depends on
> whether you think of the input to the program, e.g. the environment,
> command-line arguments, etc. as 'constant' and in some sense, pure.
> The latter are constant in the sense that they never change, but they
> are not fixed at compile-time. Other languages effectively treat them
> as pure (by passing them directly to main), whereas Haskell chooses
> not to, which is probably the reason why getArgs has IO in its type
> (something that seems unintuitive at first.)
>
> That precedent supports the view that e.g. a command-line flag
> shouldn't affect behavior without the type reflecting it, e.g. by
> doing IO, but the de facto use of the unsafe IO trick means not
> everyone agrees.
>

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Martin Odersky on "What's wrong with Monads"

amindfv
In reply to this post by Dominique Devriese-2

In practice, the amount of time you have to spend testing each function, to make sure its IO doesn't trip up in some corner case, is usually greater than the amount of time a rewrite-for-IO would take.

Tom

On Jun 28, 2012 2:54 PM, "Dominique Devriese" <[hidden email]> wrote:
2012/6/27 Tillmann Rendel <[hidden email]>:
> MightyByte wrote:
>>
>> Of course every line of your program that uses a Foo will change if you
>> switch
>> to IO Foo instead.
>
>
> But we often have to also change lines that don't use Foo at all. For
> example, here is the type of binary trees of integers:
>
>  data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)
>
> A function to add up all integers in a tree:
>
>  amount:: Tree -> Integer
>  amount (Leaf x) = x
>  amount (Branch t1 t2) = amountt1 + amountt2
>
> All fine so far. Now, consider the following additional requirement: "If the
> command-line flag --multiply is set, the function amount computes the
> product instead of the sum."
>
> In a language with implicit side effects, it is easy to implement this. We
> just change the third line of the amount function to check whether to call
> (+) or (*). In particular, we would not touch the other two lines.
>
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

I may be missing the point here, but having worked on large code bases
with a wide variety contributors before, I find it very advantageous
that programmers are prevented from writing an amount function whose
behaviour depends on command line arguments without at least an
indication in the type. The fact that the function can not perform
stuff like that is precisely the guarantee that the Haskell type gives
me...

Dominique

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
12