Re: [Haskell-cafe] Why is $ right associative instead ofleftassociative?

Quoting Paul Hudak
: Actually, one of the main reasons that we chose (:) is that that's what Miranda used. So, at the time at least, it was not entirely clear what the "de facto universal inter-language standard" was.
Phil Wadler argued for the ML convention at the time, and wrote a document containing a fair amount of sample code to illustrate what it would look like. We noticed something surprising: instead of (x:xs) and the like, Phil had consistently written (x :: xs) -- note the extra spaces. Somehow, using the longer operator name led him to feel spaces were needed around it. That in turn made his lines longer, encouraged him to split definitions across lines, and so on. When I read the thing, I realised after a while that I was skipping all the code fragments -- because they just looked too big and complicated to take in during a quick reading. It was at least partly that experience that convinced us that using :: for cons would impose a small cost, but a real one, on readability. It may seem trivial, but the sum of many such decisions is significant. The story does illustrate the importance of actually trying out syntactic ideas and seeing how they play--one can be surprised by the result.
I don't think Haskell Prime should be about changing the look and feel of the language.
It's about consolidating the most important extensions into the standard, isn't it? Changes that break existing code should be very, very well motivated--if porting code to Haskell Prime is too difficult, people just won't do it. John

John Hughes wrote:
Quoting Paul Hudak
: Actually, one of the main reasons that we chose (:) is that that's what Miranda used. So, at the time at least, it was not entirely clear what the "de facto universal inter-language standard" was.
Phil Wadler argued for the ML convention at the time, and wrote a document containing a fair amount of sample code to illustrate what it would look like. We noticed something surprising: instead of (x:xs) and the like, Phil had consistently written (x :: xs) -- note the extra spaces. Somehow, using the longer operator name led him to feel spaces were needed around it. That in turn made his lines longer, encouraged him to split definitions across lines, and so on. When I read the thing, I realised after a while that I was skipping all the code fragments -- because they just looked too big and complicated to take in during a quick reading. It was at least partly that experience that convinced us that using :: for cons would impose a small cost, but a real one, on readability. It may seem trivial, but the sum of many such decisions is significant. The story does illustrate the importance of actually trying out syntactic ideas and seeing how they play--one can be surprised by the result.
And at the time I agreed with you. But now I'm older and wiser(?). I now think :: for type signatures was a bad mistake. I don't use lists very much. They are not the right data structure for many things. So : is not as common as :: in my code. I checked a small sample of code, about 20000 lines of Haskell. It has about 1000 uses of ':' and 2000 of '::'. In my opinion all the special syntactic sugar for lists should go away. I don't think lists are special enough to motivate it. But this is not what Haskell' is about. It's supposed to be some modest extensions to Haskell. Not designing a new perfect language. -- Lennart

Hello Lennart, Sunday, February 05, 2006, 5:12:48 PM, you wrote: LA> In my opinion all the special syntactic sugar for lists should go LA> away. I don't think lists are special enough to motivate it. i have proposal (not for Haskell', of course) of using ":" and "[]" syntax for general notion of traversable collections: JM> What do people think about seperating the class and type namespaces? one i have exactly opposite idea - give the class and type names equal rights :) instead of writing foo :: (Num a, Monad m) => a -> m () allow to write foo :: Num -> Monad () and vice versa - instead of bar :: Int -> [Int] allow to write bar :: (Int a, [] b) => a -> b a That this gives? 1) significantly simplifies declarations using typeclasses. i was seriously bitten by those huge declarations, and think that simplification in this area will lead to much wider use of type classes by the ordibary users (like me :) . ideally, i just don't need to think whether a Foo is a class or type in most cases - both can be used interchangeably (like interfaces and classes in Java) 2) this allows to refactor existing code without changing type signatures. just for example - imagine that [] is now a typeclass implementing only several basic operations, namely head/tail/(:)/null. nevertheless, all those huge number of list-processing functions still work because [] in their type signatures now means that parameter/result belong to the some instance of this class. cool? i think so :) of course, that also need possibility to define pattern matching (meaning of []/a:b) inside this class, but that's a different proposal :) two uses of the same class in one declaration should mean the same type, as in: reverse :: [a] -> [a] what is equivalent to reverse :: ([] c) => c a -> c a Of course, refactoring of [] is just amazing example. what i basically mean - when program grows and some T becomes an interface instead of type, there should be no need to change all the T usages - they will continue to work, work and work. The only exception will be type signatures, where T is used ywo times or more and different T's usages can mean different types. in this case, we need to switch to expanded signature, what nevertheless should work even if T is still just a type: cvt :: (T a, T b) => a->b JM> this would allow things like one to have a class and a data type holding JM> an arbitrary member of said class to have the same name _may be_, my proposal can even solve your problem -- Best regards, Bulat mailto:bulatz@HotPOP.com

Bulat Ziganshin wrote:
LA> In my opinion all the special syntactic sugar for lists should go LA> away. I don't think lists are special enough to motivate it.
i have proposal (not for Haskell', of course) of using ":" and "[]" syntax for general notion of traversable collections:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor. [] and all its variants, however, are special syntax. -Paul

Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
But Haskell 98 does treat it specially: you can't import Prelude hiding ((:)), or rebind it locally, or refer to it as Prelude.:. In fact I've always wondered why it was done this way. Can anyone enlighten me? Of course it might be confusing if it were rebound locally, but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs). It might be kind of nice if the list type were actually defined in the Prelude as data List a = Nil | a : List a and all of the special [] syntax defined by a desugaring to this (entirely ordinary) datatype, e.g. [1,2] -> 1 Prelude.: 2 Prelude.: Prelude.Nil. -- Ben

Ben Rudiak-Gould wrote:
Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
<snip> ... but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
Can you explain why? On page 258 of Paul Hudak's book "The Haskell School of Expression" he states that do x<- xs; return (f x) is equivalent to [f x | x <- xs] "which is clearly just map f xs" I can't find anything wrong with the example in the book but perhaps I've missed something? Regards, Brian.

Brian Hulley wrote:
Ben Rudiak-Gould wrote:
Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
<snip> ... but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
Can you explain why? On page 258 of Paul Hudak's book "The Haskell School of Expression" he states that do x<- xs; return (f x) is equivalent to [f x | x <- xs] "which is clearly just map f xs"
I can't find anything wrong with the example in the book but perhaps I've missed something?
He may mean that if you *redefine* the operator Prelude.((:)) then the desugaring and other steps may end up binding the old or the new (:) and no longer be identical. This is touched on in http://www.haskell.org/ghc/docs/6.4.1/html/users_guide/syntax-extns.html#reb... In particular, if you redefine Monad, then [ f x | x<-xs ] and do {x<-xs; return x} may no longer mean the same thing.

Chris Kuklewicz wrote:
Brian Hulley wrote:
Ben Rudiak-Gould wrote:
Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
<snip> ... but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
Can you explain why? On page 258 of Paul Hudak's book "The Haskell School of Expression" he states that do x<- xs; return (f x) is equivalent to [f x | x <- xs] "which is clearly just map f xs"
I can't find anything wrong with the example in the book but perhaps I've missed something?
He may mean that if you *redefine* the operator Prelude.((:)) then the desugaring and other steps may end up binding the old or the new (:) and no longer be identical. This is touched on in
http://www.haskell.org/ghc/docs/6.4.1/html/users_guide/syntax-extns.html#reb...
In particular, if you redefine Monad, then [ f x | x<-xs ] and do {x<-xs; return x} may no longer mean the same thing.
Thanks. I didn't realise that list comprehension syntax was not just a sugar for the equivalent do expression but I see now that it is indeed translated differently, bypassing monads altogether, which of course is confusing but understandable if monads were only thought up after list comprehensions were already in the language. Regards, Brian.

Brian Hulley wrote:
... I didn't realise that list comprehension syntax was not just a sugar for the equivalent do expression but I see now that it is indeed translated differently, bypassing monads altogether, which of course is confusing but understandable if monads were only thought up after list comprehensions were already in the language.
Here's the brief history with respect to this issue: Apr 1990: Haskell Report 1.0, list comps, but no monads. May 1996: Haskell Report 1.3, monads appear. Apr 1997: Haskell Report 1.4, list comps made applicable to arbitrary monads. Feb 1999: Haskell 98 Report, list comps reverted to just lists. -Paul

Chris Kuklewicz wrote:
Brian Hulley wrote:
Ben Rudiak-Gould wrote:
... but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
Can you explain why? On page 258 of Paul Hudak's book "The Haskell School of Expression" he states that do x<- xs; return (f x) is equivalent to [f x | x <- xs] "which is clearly just map f xs"
I can't find anything wrong with the example in the book but perhaps I've missed something?
He may mean that if you *redefine* the operator Prelude.((:)) then the desugaring and other steps may end up binding the old or the new (:) and no longer be identical. This is touched on in
http://www.haskell.org/ghc/docs/6.4.1/html/users_guide/syntax-extns.html#reb...
In particular, if you redefine Monad, then [ f x | x<-xs ] and do {x<-xs; return x} may no longer mean the same thing.
Right, but the original question is whether or not [f x | x <- xs] is the same as map f xs. My book's been out for six years and no one has mentioned this issue, so if it's a problem I'd like to know why so that I can add it to my "Errata" list! Thanks, -Paul

I think the point was that all syntax (like list comprehensions or pattern matching) in Haskell is tied directly to the Prelude. So [ f x ...] is ALWAYS using the Prelude definitions of things while "map" could be hidden and redefined. The inability to change the meaning of constructs expanded from syntax as considered a bug by some, a feature by others. And I don't rember where Paul stood on this ... And the change between list / monad comprehensions was debated intensly by the committee at every step. The only real argument against monad comprehensions was that they confuse beginners with overloading. John

John Peterson wrote:
I think the point was that all syntax (like list comprehensions or pattern matching) in Haskell is tied directly to the Prelude. So [ f x ...] is ALWAYS using the Prelude definitions of things while "map" could be hidden and redefined.
Yes, of course. I was implicitly assuming that we were talking about Prelude's map.
The inability to change the meaning of constructs expanded from syntax as considered a bug by some, a feature by others. And I don't rember where Paul stood on this ...
It has always seemed to me that there should be a way to define something as syntactic expansion into things that cannot be redefined, otherwise the language definition becomes vague. -Paul

Ben Rudiak-Gould wrote:
Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
But Haskell 98 does treat it specially: you can't import Prelude hiding ((:)), or rebind it locally, or refer to it as Prelude.:. In fact I've always wondered why it was done this way. Can anyone enlighten me?
I think that originally it was because various primitives were defined (via "Translations" in the Haskell Report) in terms of lists. But with qualified imports I'm also not sure why this is necessary.
Of course it might be confusing if it were rebound locally, but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
It's not? Hmmm... why not? (At one time list comprehensions were another way to write do notation -- i.e. they were both syntactic sugar for monads -- in which case these would surely be different, but that's not the case in Haskell 98, as far as I know.) -Paul

On Sun, Feb 05, 2006 at 06:50:57PM +0000, Ben Rudiak-Gould wrote:
Paul Hudak wrote:
Minor point, perhaps, but I should mention that : is not special syntax -- it is a perfectly valid infix constructor.
But Haskell 98 does treat it specially: you can't import Prelude hiding ((:)), or rebind it locally, or refer to it as Prelude.:. In fact I've always wondered why it was done this way. Can anyone enlighten me? Of course it might be confusing if it were rebound locally, but no more confusing than the fact that [f x | x <- xs] is not the same as (map f xs).
It might be kind of nice if the list type were actually defined in the Prelude as
data List a = Nil | a : List a
and all of the special [] syntax defined by a desugaring to this (entirely ordinary) datatype, e.g. [1,2] -> 1 Prelude.: 2 Prelude.: Prelude.Nil.
it would probably be simpler just to declare [] to be a data constructor. that is what jhc does, it parses the same as any capitalized name. so you can do import Prelude hiding([]) data Foo a = [] | Foo | Bar and list syntax desugars into whatever (:) and [] are in scope. similarly, (x,y) is just sugar for (,) x y and (,) is a standard data constructor and can be hidden, redefined, etc just like any other one. John -- John Meacham - ⑆repetae.net⑆john⑈

On Sun, Feb 05, 2006 at 09:12:48AM -0500, Lennart Augustsson wrote:
I now think :: for type signatures was a bad mistake. I don't use lists very much. They are not the right data structure for many things.
In my opinion all the special syntactic sugar for lists should go away. I don't think lists are special enough to motivate it.
I was going to disagree with you, but maybe you are right. Lists are very special (IMO), but we could live without the special [] syntax. But I guess we will agree to have some kind of special syntax for list comprehensions, be it []-comprehensions or monad comprehensions. PS. Lisp people are laughing at us, but I don't care ;-) Best regards Tomasz -- I am searching for programmers who are good at least in (Haskell || ML) && (Linux || FreeBSD || math) for work in Warsaw, Poland

On Sun, 5 Feb 2006, Lennart Augustsson wrote:
I don't use lists very much. They are not the right data structure for many things.
Certainly, but lists are useful as interim data structure or for initialising complex data structures.
So : is not as common as :: in my code. I checked a small sample of code, about 20000 lines of Haskell. It has about 1000 uses of ':' and 2000 of '::'.
In my opinion all the special syntactic sugar for lists should go away. I don't think lists are special enough to motivate it.
Fine, someone shares my attitude towards the list sugar. Nevertheless, do you mean with 'no sugar for lists' also no infix operator for list construction? I would still like an operator of low precedence for list construction for writing e.g. (1,'a"):(2,'b'):[].
But this is not what Haskell' is about. It's supposed to be some modest extensions to Haskell. Not designing a new perfect language.
Yes, this discussion is definitely beyond Haskell'.

Hello Henning, Monday, February 06, 2006, 4:12:44 PM, you wrote:
In my opinion all the special syntactic sugar for lists should go away. I don't think lists are special enough to motivate it.
HT> Fine, someone shares my attitude towards the list sugar. Nevertheless, do HT> you mean with 'no sugar for lists' also no infix operator for list HT> construction? I would still like an operator of low precedence for list HT> construction for writing e.g. (1,'a"):(2,'b'):[]. i prefer to have ":" and "[]" as general collection constructors: class Collection c a where [] :: c a -- creates empty collection (:) :: a -> c a -> c a -- adds value to the head of collection and having "default rules" that instatiates this collection type to list if there is no type signatures and other information what allows to find proper type of collection constructed this way - just like the "default Int" language construct defaults all untyped numeric constants to Int -- Best regards, Bulat mailto:bulatz@HotPOP.com
participants (11)
-
Ben Rudiak-Gould
-
Brian Hulley
-
Bulat Ziganshin
-
Chris Kuklewicz
-
Henning Thielemann
-
John Hughes
-
John Meacham
-
John Peterson
-
Lennart Augustsson
-
Paul Hudak
-
Tomasz Zielonka