|
Cafe, I was watching a panel on languages[0] recently and Martin Odersky (the creator of Scala) said something about Monads: "What's wrong with Monads is that if you go into a Monad you have to change your whole syntax from scratch. Every single line of your program changes if you get it in or out of a Monad. They're not polymorphic so it's really the old days of Pascal. A monomorphic type system that says 'well that's all I do' ... there's no way to abstract over things. " [0, 53:45]
Thoughts? --J Arthur _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
False!
You only have to change the parts of the program that need the effect that the monad provides. A well designed program will likely have much of its code in pure libraries. Think of the monadic code as a "scripting language" that you bind your libraries together with to make the program. On 24 June 2012 06:31, Jonathan Geddes <[hidden email]> wrote: > Cafe, [SNIP] > > "What's wrong with Monads is that if you go into a Monad you have to change > your whole syntax from scratch. Every single line of your program changes if > you get it in or out of a Monad. [SNIP] > > Thoughts? > _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Jonathan Geddes
"What's wrong with Monads is that if you go into a Monad you have to
change your whole syntax from scratch. Every single line of your program changes if you get it in or out of a Monad. They're not polymorphic so it's really the old days of Pascal. A monomorphic type system that says 'well that's all I do' ... there's no way to abstract over things. " [0, 53:45] [0] - http://css.dzone.com/articles/you-can-write-large-programs I think the context of the question is important here. Odersky is asked why provide all this elegant machinery for doing functional things while avoiding the difficult/critical parts -- the parts that deal with the effects. Odersky seems to be making three claims. * To move between functional and monadic code you have to completely rewrite the code procedurally -- its true and (IMHO) regrettable. * Monadic code is monomorphic: this appears to be seriously mistaken. Monadic functions can be as polymorphic as any non-monadic functions. (I have never wished the lambda-bound variables introduced by 'do' statements were somehow polymorphic.) * There is no way to abstract over monadic code: this also appears to be mistaken as there are plenty of ways of abstracting while writing monadic code (using the very same techniques you would use for non-monadic code). Monads allow procedural code to be expressed procedurally and functional code to be expressed functionally and the type system ensures there are no mix ups. Expressing procedural code functionally is as unnatural and error prone as expressing functional code procedurally in my experience -- that Haskell avoids compelling the programmer to do either within its strongly-typed functional framework is (IMHO) its great invention(*) and enduring strength. Maybe someday someone will devise a way writing 'effects' code in a strongly-typed functional framework that doesn't force the programmer to commit each function to being either procedural or functional -- or better yet, do away with the need to write any effects code. Perhaps it has been done already. (I don't doubt folks have claimed to have done it.) But until there is a proven language and tools that can do this, monads look (to me, at least) like the best method for attacking 'effects' within a strong functional framework. Chris (*) Monads were invented within the Haskell framework of course, after the publication of the early reports and tools. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On Sun, Jun 24, 2012 at 12:27 AM, Chris Dornan <[hidden email]> wrote:
It's false. do-notation is completely optional. It merely makes it easier to extract multiple values from monadic actions, instead of the basic "one value per step" bind provides. Using join and (>>=) is just as easy as do-notation, once you understand the idiom.
A monad is, first and foremost, a functor. You can get at the underlying algebra while statically ensuring relevant properties, using its functorial or applicative or monadic interfaces. For example: consider a function that parses a string into a Foo. It will have a type like parseFoo :: String -> Maybe Foo Should "useFoo" have the type
useFoo :: Maybe Foo -> Maybe Bar or useFoo :: Foo -> Bar? The maybe functor/monad encapsulates the behavior of propagating possible-undefinedness, so we are entirely justified in using its functor/monad interface to simplify our types. There is no reason to redundantly have to pattern match on (Just foo) or (Nothing) when we can abstract the control structure away and do
useFoo <$> (parseFoo "Foo 1") -- (<$> == fmap) or (parseFoo "Foo 1") >>= return . useFoo Expressing procedural code Haskell didn't invent monads. They have been in use since Grothendieck's work in algebraic topology. At least! It is not hard to make a plausible case that the Pythagoreans had a theory of monads in mind 2500 years ago.
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Chris Dornan
> To move between functional and monadic code you have to completely rewrite the code procedurally
I don't like the way people segregate "pure" from "monadic". Monadic code *is* pure: it's written with completely pure Haskell and completely pure combinators. As Alexander said, it's really important to say that 'do' is just syntax over abstract combinators, and that it's *only for IO/ST* that those combinators have something special. This is why I do disagree with tutorials who begin by showing the 'do' notation saying "do IO with that". If the beginner reads that and then puts his learning aside (because he was interested to get some input about Haskell but has no time to continue for the moment), he walks away with the false impression that you have do blocks to do imperative stuff, and for the rest you just use normal functions. People are there because they've heard about "the almighty purely functional Haskell", I say let's give 'em what they came for: getLine >>= \x -> putStrLn ("Hello " ++ x ++ "!") It's not complicated to understand the concept of an operator retrieving the result of one operation and injecting it in the next. Twist the syntax as you want if you think lambdas are not readable for beginners, but still, if we begin by that, develop a little with a few more examples and then say something like: "But it would be very cumbersome to have to write everything like that. But don't worry, with great purity does not come great integrism! Haskell people have been intelligent and brought you a special syntax to write it more legibly, but it's actually exactly the same you're doing!" then the intuition that "monads are just like pure code" comes by itself. This might be what leads people like Martin Odersky to false conclusions. Amongst Haskellers I understand that we use this shortcut (pure VS monadic) but as I said, towards non-Haskellers it's IMHO a bad idea to start rightaway by making the difference between both. Now, concerning Odersky's sentence specifically: "To move between functional and monadic code you have to completely rewrite the code procedurally": I don't see how that differs from any other language: monads are just patterns (*), if you start with one method and then switch to another then you'll have to refactor. I'd say monads are actually better in that respect, since they can abstract a lot due to their inherent EDSL nature (provide just one type and its set of operations, and then you can change everything under the hood). (*) And because of their "pattern" nature, they have the same drawbacks than patterns in OO: people may (and will) try to fit triangles in round holes, i.e. to use a monad where another abtraction (or no abstraction at all) would be better. 2012/6/24 Chris Dornan <[hidden email]>
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
do-notation is just syntax in theory but in practice
it's difficult to avoid it. Try to write any OpenGL program in terms of `>>=`, `>>` and `return`. I don't like to use `do` knowing that it's just syntax sugar but sometimes it help a lot. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Jonathan Geddes
Here is an half-baked idea how to make monads more functional.
It's too wild to be implemented in haskell. But maybe you are interested more in ideas than implementations, so let's start with monad class class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b I think monad's methods are misleading, let's rename them class Monad m where idM :: a -> m a (*$) :: (a -> m b) -> m a -> m b We can see that `return` is a monadic identity and the `bind` is an application in disguise. So now we have two applications. It's standard `($)` and monadic `(*$)`. But they are application. Well isn't it something like `plusInt` and `plusDouble`? Maybe we can devise single class for application. Let's imagine a special class `App` class App ?? where ($) :: ??? As you can see it's defined so that we can fit monads and plain functions in this framework. Moreover if we redefine this class than whitespace is redefined automatically! So `($)` really means *white space* in haskell. `idM` is interesting too. In standard world we can safely put `id` in any expression. So when we write f = a + b we can write f = id (a + b) or even f = id ((id a) + (id b)) meaning doesn't change. So if we have special class `Id` class Id f where id :: ??? Again you can see that monads fit nicely in the type. Why do we need this class? Whenever compiler gets an type mismatch, it tries to apply method from `Id` class, if it's defined ofcourse. But we have a class called `Category`, `id` belongs to it: class Category (~>) where id :: a ~> a (>>) :: (a ~> b) -> (b ~> c) -> (a ~> c) Let's pretend that `(>>)` is reversed composition `(.)`. It's interesting to note that there is another formulation of 'Monad' class. It's called Kelisli category. class Kelisli m where idK :: a -> m a (>>) :: (a -> m b) -> (b -> m c) -> (a -> m c) Here again let's forget about monad's `(>>)` for a moment, here it's composiotion. `Kleisli` is equivalent to `Monad`. If we can define `Category` instance for `Kleisli`, so that somehow this classes become unified on type level we can define application in terms of composition like this: f $ a = (const a >> f) () And we can get application for monads (or kleislis :) ). Implications: Maybe someday you wrote a function like this: foo :: Boo -> Maybe Foo foo x = case x of 1 -> Just ... 2 -> Just ... 3 -> Just ... 4 -> Just ... 5 -> Just ... 6 -> Just ... 7 -> Just ... _ -> Nothing with `idM` rule you can skip all Just's You can use white space as monadic bind. So functional application can become monadic on demand. Just switch the types. Implementation: I've tried to unify `Category` and `Kleisli` with no luck. Here is a closest sletches: simplest sketch requires type functions :( instance Monad m => Category (\a b -> a -> m b) where ... the other one too :( class Category (~>) where type Dom (~>) :: * -> * type Cod (~>) :: * -> * id :: Dom (~>) a -> Cod (~>) a (>>) :: (Dom (~>) a ~> Cod (~>) b) -> (Dom (~>) b ~> Cod (~>) c) -> ... instances type Id a = a -- :( instance Monad m => Category (a -> m b) where type Dom (a -> m b) = Id type Cod (a -> m b) = m ... _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Alexander Solla
> > To move between functional and monadic code you have
> > to completely rewrite the code procedurally > It's false. do-notation is completely optional. It merely makes it > easier to extract multiple values from monadic actions, instead > of the basic "one value per step" bind provides. Using join > and (>>=) is just as easy as do-notation, once you understand the idiom. Odersky's point (and mine) was about moving between monadic and functional code, not eliminating a do notation (which is indeed a fairly trivial syntactic device). To take a fake and absurd example, there is a world of difference between add:: Double -> Double-> IO Double and the stock addition operator. (Perhaps you need to be very careful about exceptions.) If you structure your program so that certain kinds of arithmetic has to be done monadically then everything that uses these operations must be written quite differently from how it would be with simple arithmetic operations. You can argue that well it's just in the types -- you pays your money and takes you choice. (Viz., if your function works with effects then it should be expressed in its type.) But then this becomes the price of doing everything in a strong functional framework. To take one counter example, the Standard ML combines functional programming and effects without forcing this reification on the programmer. I much prefer the Haskell way. But when it get criticized by a non-Haskellers I try to understand the criticism in the context it was phrased. > Haskell didn't invent monads. Indeed, that sloppy phrasing on my part. I meant Haskell invented 'monads' as they have come to be understood in the Haskell context (a general programming device for encapsulating effects-based code in a functional programming context), not the original algebraic construct or its mathematical applications. Chris _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On Sun, Jun 24, 2012 at 2:48 AM, Chris Dornan <[hidden email]> wrote:
I sort of see where you're coming from. But I'm having a hard time seeing how this "complaint" would work with respect to Maybe and the other pure monads. In other words, I suspect the problem you're describing is particular to IO and IO-like monads.
If a function isn't total, it is entirely natural to use "Maybe" to encapsulate the potential for undefinedness. I would be extremely hesitant to call mAdd :: Int -> Int -> Maybe Int
"monadic" unless it was actually using a monadic interface to Maybe. Indeed, I would call it "pure" and "functional". Similarly for: (++) :: [a] -> [a] -> [a]
The result types are monadic insofar as they have a free type variable, but they are also entirely pure and functional.
Yes, this. If you need a list, use a list. If you need simple undefinedness, use Maybe, etc. But then this becomes the price of doing everything in a strong functional I don't know SML. How is our list "monadic" and theirs not? In particular, how is Haskell "forcing" the reification while SML does not?
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Jonathan Geddes
Odersky is repeatedly wrong on this subject and specifically for the
claim that you quote, the only response is simply "not true."
On 24/06/12 15:31, Jonathan Geddes wrote: Cafe, I was watching a panel on languages[0] recently and Martin Odersky (the creator of Scala) said something about Monads: "What's wrong with Monads is that if you go into a Monad you have to change your whole syntax from scratch. Every single line of your program changes if you get it in or out of a Monad. They're not polymorphic so it's really the old days of Pascal. A monomorphic type system that says 'well that's all I do' ... there's no way to abstract over things. " [0, 53:45] Thoughts? --J Arthur [0] - http://css.dzone.com/articles/you-can-write-large-programs_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe -- Tony Morris http://tmorris.net/ _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Anton Kholomiov
On 6/24/12 5:41 AM, Anton Kholomiov wrote:
> I think monad's methods are misleading, let's rename them > > class Monad m where > idM :: a -> m a > (*$) :: (a -> m b) -> m a -> m b > > We can see that `return` is a monadic identity and the `bind` > is an application in disguise. So now we have two applications. > It's standard `($)` and monadic `(*$)`. But they are application. > Well isn't it something like `plusInt` and `plusDouble`? > Maybe we can devise single class for application. Let's > imagine a special class `App` > > class App ?? where > ($) :: ??? > > As you can see it's defined so that we can fit > monads and plain functions in this framework. Moreover > if we redefine this class than whitespace is redefined > automatically! The class you're looking for is Applicative. The (<*>) operator handles application of "effectful" things to "effectful" things, whereas (<$>) handles the application of non-"effectful" things to "effectful" things. This situation is interesting because it highlights the fact that there is a distinction between the meaning of whitespace between function and argument vs the meaning of whitespace between argument and argument. The desire to invoke (<$>) and (<*>) implicitly is known as idiom brackets. SHE provides this as syntactic sugar, and of course you can define your own version using iI and Ii as your "brackets". > So `($)` really means *white space* in haskell. Not entirely so, as noted above. Though yes, ($) denotes application in just the same way as function--argument whitespace does. However, the presence of ($) as an operator is helpful because it allows us to capture the syntax of our language, as in: fmap ($x) fs zipWith ($) fs xs ... -- Live well, ~wren _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Alexander Solla
On 24 June 2012 18:46, Alexander Solla <[hidden email]> wrote:
Yes
this problem is specific to IO-based functions. If you didn't know
anything about monads yet would have written a Maybe/Either function
then the types are identical to the monadic formulation, and the monadic
framework in this case is just helping you to structure everything.
Unless this structucture is obscuring or confusing matters (and I don't
see it) its difficult to imagine any objection here.
In SML you can put side-effecting computaions in 'pure' functions -- functions whose type doesn't reveal that there are side effects. In Haskell terms, every function is actually in the IO monad -- or every function is given carte blanche to use unsafePerformIO depending upon how you look at it. In semantic terms it is really a case of the former; from a programming perspecive it is more like the latter; Standard ML is strict and I am pretty sure this is only practical in a strict language. It is (IMHO) deeply horrible, and possibly justifiable before monadic I/O was invented (but not for me). I am not advocating doing this (at all) but using it to illustrate a point. In standard ML you can start doing effect-based things inside a function without having to alter its type and they type of everything that uses it, and so on. ![]() Chris _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
> In standard ML you can start doing effect-based things inside a function without having to alter its type and they type of everything that uses it, and so on.
This in turn causes a break-down in the type-system where weak type variables are introduced. We can see the pathological case for that here: http://www.cs.washington.edu/education/courses/cse505/00au/lectures/13-refs.txt This makes regular things like curried function compositions unusable, because their polymorphicness goes away! Such is the result of not encapsulating side-effects. —Arlen _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Tony Morris-4
On 24 June 2012 22:38, Tony Morris <[hidden email]> wrote:
My point is this.
Making sense of part of what is being said and agreeing with it are quite different -- never mind agreeing with the wider point. Chris _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by wren ng thornton
The class you're looking for is Applicative. The (<*>) operator
handles application of "effectful" things to "effectful" things, whereas
(<$>) handles the application of non-"effectful" things to
"effectful" things. This situation is interesting because it highlights
the fact that there is a distinction between the meaning of whitespace
between function and argument vs the meaning of whitespace between
argument and argument.
`Applicative` is not enough for monads. `Applicative` is like functor only for functions with many arguments. It's good for patterns: (a -> b -> c -> d) -> (m a -> m b -> m c -> m d) Monads are good for patterns (a -> b -> c -> m d) -> (m a -> m b -> m c -> m d) So I can not express it with `Applicative`. My analogy really breaks down on functions with several arguments, since as you have pointed out there are two white spaces. But I like the idea of using one sign for normal and monadic and maybe applicative applications. Anton _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
My pocket explanation:
While e a function gives one only value of the codomain for each element of the domain set (and thus it can be evaluated a single time), a category is a generalization that accept many graphs that goes from each element of the domain to the codomain. For that matter getChar can be understood mathematically only using cathegory theory. To discover where the chain of graphs goes each time, it is necessary to execute the chain of sentences.
Imperative languages works "categorically" every time, so they don´t need an special syntax for doing so. Haskell permits to discriminate functional code from "normal" "categorical/imperative" code, so the programmer and the compiler can make use of the mathematical properties of functions. For example, graph reduction thank to the uniqueness of the paths.
Besides that, everything, functional or monadic is equally beatiful and polimorphic. i don´t think that monadic code is less fine. It is unavoidable and no less mathematical.
2012/6/25 Anton Kholomiov <[hidden email]>
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Well,
Monads are something optional at the end. Even the IO Monad is an optional pattern with unsafePerformIO, but we use it because one of the reasons we love Haskell is it's ability to differentiate pure and impure functions. But sadly this is one of the traits we love about Haskell but others dislike about it. Cheers, Ernesto Rodriguez On Mon, Jun 25, 2012 at 2:23 PM, Alberto G. Corona <[hidden email]> wrote: My pocket explanation: _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Jonathan Geddes
Jonathan Geddes <geddes.jonathan <at> gmail.com> writes:
> Cafe, > > I was watching a panel on languages[0] recently and Martin Odersky (the creator of Scala) said something about Monads: > > > "What's wrong with Monads is that if you go into a Monad you have to change your whole syntax from scratch. Every single line of your program changes if you get it in or out of a Monad. They're not polymorphic so it's really the old days of Pascal. A monomorphic type system that says 'well that's all I do' ... there's no way to abstract over things. " [0, 53:45] > > > Thoughts? In my mind, his first sentence could be paraphrased as follows: "What's wrong with having a strongly typed mechanism for separating pure and effectful code is that pure code and effectful code are actually separate." Of course every line of your program that uses a Foo will change if you switch to IO Foo instead. If the two were indistinguishable, then you'd have an impure language instead of a pure one. Furthermore, this isn't a characteristic specific to monads, it's the same for any type constructor whether it's a monad or not. To Martin's credit, earlier in the talk (around 45:15) he makes the following statement: "What you need in the language is the ability to essentially reduce state. And potentially what Scala doesn't do, but for instance Haskell does is to control state and purity, and I think that might be the next frontier." Then at 50:15 he says, "So right now, I think...in the world of effect checking we're about where we were with Pascal in terms of static typing." It seems that his heart is in the right place, but he isn't aware of where we actually are right now, and probably doesn't fully appreciate the consequences of strongly typed purity. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
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 [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
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:
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
| Free forum by Nabble | Edit this page |
