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

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. 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 '::'.
Just for interest, I analysed some of my code. Obviously my style is quite different to yours--my type specialiser of 3,500 lines has 240 conses, and only 22 occurrences of '::'. I seem to be using '::' a bit more lately, though, which I suspect is due to using classes much more. I also checked the Agda source code, about 14,000 lines, with about 500 occurrences of cons and 640 of '::'. I think the only conclusion one can draw is that style varies.
In my opinion all the special syntactic sugar for lists should go away. I don't think lists are special enough to motivate it.
What, no list comprehensions?? I'd disagree--sequencing is special, and lists represent it directly. Don't forget, also, that lists are also much more prevalent in beginners' code--and nice notation for beginners helps get people started on Haskell.
But this is not what Haskell' is about. It's supposed to be some modest extensions to Haskell. Not designing a new perfect language.
Right! John

John Hughes wrote:
What, no list comprehensions??
No. I think the do notation is good enough.
I'd disagree--sequencing is special, and lists represent it directly. Don't forget, also, that lists are also much more prevalent in beginners' code--and nice notation for beginners helps get people started on Haskell.
I don't really see what's so much better about writing [x1,x2,x3,x4,x5] than x1:x2:x3:x4:x5:[]. When I've explained lists to beginners I've just found it annoying and hard to explain why there are two ways of writing lists. And why only lists have this special syntax. -- Lennart

On Sun, Feb 05, 2006 at 10:45:50AM -0500, Lennart Augustsson wrote:
I don't really see what's so much better about writing [x1,x2,x3,x4,x5] than x1:x2:x3:x4:x5:[]. When I've explained lists to beginners I've just found it annoying and hard to explain why there are two ways of writing lists. And why only lists have this special syntax.
-- Lennart
But if you remove the [...] syntax, there will be more :'s in people's code. You are working against yourself here ;-) 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 05/02/06, Lennart Augustsson
John Hughes wrote:
What, no list comprehensions??
No. I think the do notation is good enough.
I'd disagree--sequencing is special, and lists represent it directly. Don't forget, also, that lists are also much more prevalent in beginners' code--and nice notation for beginners helps get people started on Haskell.
I don't really see what's so much better about writing [x1,x2,x3,x4,x5] than x1:x2:x3:x4:x5:[]. When I've explained lists to beginners I've just found it annoying and hard to explain why there are two ways of writing lists. And why only lists have this special syntax.
-- Lennart
Lists have special syntax because they're the lazy-functional counterpart to loops. They're quite a fundamental structure, regardless of what other data types we may have at our disposal, and I think that lots of special support is reasonable. Loops in imperative languages often get all kinds of special syntax support, and I don't think it's too far off-base to give lists special syntax accordingly. That said, I'd *really* like to see monad comprehensions come back, since they align better with the view that monads are container types, dual to the view that monads are computations, which is supported by the do-syntax. This view is actually much easier to teach (in my experience). Giving lists a little extra syntax is nice, but giving things unnecessarily restrictive types seems to be the point at which I'd consider it going too far. I haven't thought this out too carefully, but perhaps in order to give the brackets and commas syntax some more weight, the syntax x1:x2:x3:[] (or x1:x2:x3:Nil ?) could be used solely as a list, but [x1,x2,x3] would be the corresponding element in any MonadPlus -- this would be quite handy in a lot of the cases which I care about (nondeterministic computation). It would also mesh perfectly with monad comprehensions. I'd also like to mention that although my background is in pure mathematics, I had no trouble whatsoever adjusting to :: meaning "has type". A colon is commonly inserted in mathematics between the name of a function and a depiction of the domain and codomain with an arrow between them, but I wouldn't think of that as formal syntax per-se. Also, it's not centuries-old as mentioned, but only about 50 years old -- I believe it started with the use of arrows in category theory. Before then, people mostly stated the types of functions in words, or it was left completely implicit, and they still often do both of those. Also, it is only used for functions and doesn't apply to values in any set or concrete categorical object. The notation x : S to mean x is an element of S is not in widespread common use. The use and context in mathematics is sufficiently different that I don't see it as a concern that Haskell be the same in this regard. The aesthetic reason that I like :: for "has type" and : for cons is that it's far more common that type signatures occur on a line by themselves, whereas conses when needed are often needed in bunches on the same line. Not that I'm suggesting that we change things, but as an example, I actually wouldn't mind typing out "has type" for type declarations (though the symbolic form is awfully nice when things must be annotated in-line, because it looks more like a separator rather than some random words -- syntax colouring could make up for that though), whereas I would likely mind a larger symbol for list cons. The amount of typing isn't the concern, it's how it actually looks on the page, and where it occurs in use. The wishy-washy informal reasoning is that cons is like a small bit of punctuation between the elements of a list -- semantically a comma, whereas a type annotation is actually saying something. When reading code aloud, you might not even say 'cons', and if you do say something, it'll probably be something fairly short and punctuation-like whereas for a type annotation, you're almost certainly going to say 'has type' or 'is of type', which seems structurally 'larger' to me, and perhaps deserves a bigger, more noticeable representation on the page. - Cale

Cale Gibbard wrote:
That said, I'd *really* like to see monad comprehensions come back, since they align better with the view that monads are container types, dual to the view that monads are computations, which is supported by the do-syntax. This view is actually much easier to teach (in my experience). Giving lists a little extra syntax is nice, but giving things unnecessarily restrictive types seems to be the point at which I'd consider it going too far.
The trouble with monad comprehensions was that it became far too easy to write ambiguous programs, even when you thought you were just working with lists. Haskell overloading works really nicely *as long as there's a judicious mixture of overloaded and non-overloaded functions*, so that the overloading actually gets resolved somewhere. Overload too many things, and you end up having to insert type annotations in the middle of expressions instead, which really isn't nice. Lists are special, not least because they come very early in a Haskell course--or, in my case, in the first ever programming course my students have ever taken. Getting error messages about ambiguous overloading when they are still trying to understand what comprehension notation means (without even the concept of a for-loop to relate it to) is very destructive. And this is in the case where the code is otherwise type-correct--the kind of error message you would get by trying to append a number to a monad comprehension doesn't bear thinking about! The class system is already something of an obstacle in teaching, because you have to mention it in the context of arithmetic (even if you tell students always to write monomorphic type signatures, they still see classes mentioned in error messages). After all, that is surely why Helium doesn't have it. I find classes manageable for arithmetic, even if students do take some time to learn to distinguish between a class and a type (or a type and a value, for that matter!). But it's a relief that list programs, at least, have simple non-overloaded types. List functions provide an opportunity to introduce polymorphism in a simple context--it's much easier to understand why (++) should have the type [a] -> [a] -> [a], than to start talking about MonadPlus m => m a -> m a -> m a. There is a lot to learn in Haskell, especially in the type and class system. It's an advantage if you don't have to learn it all at once--if you can master lists and list comprehensions without exposure to monads (which are a much harder concept). We should never forget that beginners have somewhat different needs from expert programmers--and those needs are also important. If we want Haskell to be used for first programming courses (and it's a big advantage to catch 'em early), then there needs to be a learning path into the language that goes quite gently. Monomorphic lists help with that. We did consider more aggressive defaulting to address the ambiguity problems with monad comprehensions--defaulting Monad to lists, for example, or user-declared defaulting rules--but this introduces yet more complexity without really addressing the problem of keeping types simple for beginners, so the idea was abandoned. John

On 06/02/06, John Hughes
Cale Gibbard wrote:
That said, I'd *really* like to see monad comprehensions come back, since they align better with the view that monads are container types, dual to the view that monads are computations, which is supported by the do-syntax. This view is actually much easier to teach (in my experience). Giving lists a little extra syntax is nice, but giving things unnecessarily restrictive types seems to be the point at which I'd consider it going too far.
The trouble with monad comprehensions was that it became far too easy to write ambiguous programs, even when you thought you were just working with lists. Haskell overloading works really nicely *as long as there's a judicious mixture of overloaded and non-overloaded functions*, so that the overloading actually gets resolved somewhere. Overload too many things, and you end up having to insert type annotations in the middle of expressions instead, which really isn't nice.
Lists are special, not least because they come very early in a Haskell course--or, in my case, in the first ever programming course my students have ever taken. Getting error messages about ambiguous overloading when they are still trying to understand what comprehension notation means (without even the concept of a for-loop to relate it to) is very destructive. And this is in the case where the code is otherwise type-correct--the kind of error message you would get by trying to append a number to a monad comprehension doesn't bear thinking about!
The class system is already something of an obstacle in teaching, because you have to mention it in the context of arithmetic (even if you tell students always to write monomorphic type signatures, they still see classes mentioned in error messages). After all, that is surely why Helium doesn't have it. I find classes manageable for arithmetic, even if students do take some time to learn to distinguish between a class and a type (or a type and a value, for that matter!). But it's a relief that list programs, at least, have simple non-overloaded types. List functions provide an opportunity to introduce polymorphism in a simple context--it's much easier to understand why (++) should have the type [a] -> [a] -> [a], than to start talking about MonadPlus m => m a -> m a -> m a.
There is a lot to learn in Haskell, especially in the type and class system. It's an advantage if you don't have to learn it all at once--if you can master lists and list comprehensions without exposure to monads (which are a much harder concept). We should never forget that beginners have somewhat different needs from expert programmers--and those needs are also important. If we want Haskell to be used for first programming courses (and it's a big advantage to catch 'em early), then there needs to be a learning path into the language that goes quite gently. Monomorphic lists help with that.
We did consider more aggressive defaulting to address the ambiguity problems with monad comprehensions--defaulting Monad to lists, for example, or user-declared defaulting rules--but this introduces yet more complexity without really addressing the problem of keeping types simple for beginners, so the idea was abandoned.
John
How about a compiler switch for beginners (maybe with an included script that adds it to the command line) that turns off a bunch of the more complex issues involved, and uses a beginner's version of the Prelude? Helium exists as well, which is a simplified version of Haskell for beginners, without even typeclasses. It has very careful and detailed error messages. Having monad comprehensions actually helps with another newbie problem, albeit one which is a little farther along the garden path -- learning about monads. Defaulting monad comprehensions is probably a good idea. List comprehensions are probably the most common case anyway, just because lists are the most common container type. :) This also further helps in introducing monads as generalisations of lists. If extensions to the language get standardised, I'd be fine with having monad comprehensions among them. Adding an extension declaration or compiler switch to any module using them wouldn't be so bad either, though I'd really like them in the actual language. I'd also want to include the usual changes to the prelude. The situation with map I find especially grating. Having two versions of common functions is one thing, but 3 is getting out there! :) Is explaining Functor really that hard? It's just container types with a way to apply a function to all of their elements. :) I don't think it should be necessary to completely rule out useful features because they might be difficult to newcomers. There should always be ways to turn things off, and construct a simpler language for new users. Dr. Scheme seems to take this approach, and has a pretty fine gradation of languages for leading users from their first steps into the finer points of Scheme. (I'm not completely sure how well Dr. Scheme works in practice as I haven't used it a whole lot myself, but the idea is great, and it certainly looks quite nice.) Also, it seems that classes are one of the first things one has to teach now anyway, as they're bound to come up in error messages in any actual programs you try to write. When teaching my friend, I taught the basics of classes along with types in the first and second lesson (including how to define them) and presented various values and functions as examples. Perhaps certain error messages would still be confusing after a basic intro to classes, but I think that improving error messages would help quite a lot here. :) GHC's ability to suggest common fixes is fairly useful (though occasionally misleading). An automatic (interactive?) glossary for terms like 'unresolved overloading' would be quite good. It would also be nice to offer links in error messages to the Haskell wiki, where users could discuss common reasons and fixes for a given error. - Cale

Cale Gibbard wrote:
How about a compiler switch for beginners (maybe with an included script that adds it to the command line) that turns off a bunch of the more complex issues involved, and uses a beginner's version of the Prelude?
Well, right now, the complex issues are turned off by having separate types and separate functions, which works pretty well. Would such a switch resolve overloading silently to "beginners' types"? If so, some code would compile with the switch, but not without it--which is pretty scary. You'd need to check when compiling each module whether it was "beginners" or not.
Is explaining Functor really that hard? It's just container types with a way to apply a function to all of their elements. :)
Also, it seems that classes are one of the first things one has to teach now anyway, as they're bound to come up in error messages in any actual programs you try to write. When teaching my friend, I taught the basics of classes along with types in the first and second lesson (including how to define them) and presented various values and functions as examples.
Something tells me your friend wasn't an undergraduate of only moderate ability, with no programming experience of any kind. Honestly, the difference between a singleton list and its element is a big deal at this stage--words like "container types" would go right over almost all students' heads. You're right that classes must be mentioned early, but there's no need for more than a very basic understanding. I tell my students always to write type signatures at the start, so the error messages they see say things like "No instance Num Bool". I just tell them that means booleans aren't numbers--they don't need to even see a class or instance definition to understand that. I don't think it should be necessary to completely rule out useful features because they might be difficult to newcomers. There should always be ways to turn things off, and construct a simpler language for new users. Dr. Scheme seems to take this approach, and has a pretty fine gradation of languages for leading users from their first steps into the finer points of Scheme. It would be interesting to see a "Dr. Scheme" like approach to Haskell. But there is a risk with that approach--namely, that students get the impression (correctly) that they are learning a toy language which isn't usable for real applications. That is a big-time motivation killer. (I'm not criticizing Dr Scheme here, but rather how I imagine a flag of the sort we're discussing might work with Haskell). It's important that there is a smooth route to infinity and beyond, so one can show students cool stuff like wxHaskell without discontinuities along the way. Otherwise they're just glad to finish their Haskell course, and move on to a real programming language like Java. Separate syntax for easy and more difficult concepts--separating comprehensions from do--does provide a very smooth progression for beginners. John

Hello John, Monday, February 06, 2006, 10:39:59 AM, you wrote:
That said, I'd *really* like to see monad comprehensions come back,
JH> We did consider more aggressive defaulting to address the ambiguity JH> problems with monad comprehensions--defaulting Monad to lists, for JH> example, or user-declared defaulting rules--but this introduces yet more JH> complexity without really addressing the problem of keeping types simple JH> for beginners, so the idea was abandoned. why not allow some sort of module-wide pragma to "enable" use of this and any other features for expert programmers? just recalling last "import extension" proposal, we can add: import extension i-am-expert-programmer-enable-monad-comprehension-pleeeeease! :) -- Best regards, Bulat mailto:bulatz@HotPOP.com

On 2/6/06, John Hughes
The trouble with monad comprehensions was that it became far too easy to write ambiguous programs, even when you thought you were just working with lists.
Would the Haskell98-style solution be to add defaulting for Monads?
--
Taral

The trouble with monad comprehensions was that it became far too easy to write ambiguous programs, even when you thought you were just working with lists.
One solution was already suggested: to make the comprehension syntax be pure syntactic sugar whose semantics depends on the semantics of the identifiers the syntactic sugar expands into. So you could keep the current list-only comprehension as default, and allow monad comprehension by providing a library (which the users need to import so as to hide the Prelude's definition). Stefan

On Feb 8, 2006, at 1:34 AM, Stefan Monnier wrote:
The trouble with monad comprehensions was that it became far too easy to write ambiguous programs, even when you thought you were just working with lists.
One solution was already suggested: to make the comprehension syntax be pure syntactic sugar whose semantics depends on the semantics of the identifiers the syntactic sugar expands into.
OK. Which identifiers? I happen to want a version which always uses "concatMap" (or, equivalently, monadic bind), and never, ever the direct "efficient" translation. To get the efficient translation for lists a la Wadler, though, this requires either a wrapper, so that the comprehension runs at the type ([a] -> [a]) and gets applied to [] at the very end, or it requires heavy lifting from the compiler (foldr/build and its kin as seen in GHC, phc, etc.). When it was all tied to lists, it was easy to gloss over the details of the machinery. -Jan-Willem Maessen
So you could keep the current list-only comprehension as default, and allow monad comprehension by providing a library (which the users need to import so as to hide the Prelude's definition).
Stefan
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
participants (8)
-
Bulat Ziganshin
-
Cale Gibbard
-
Jan-Willem Maessen
-
John Hughes
-
Lennart Augustsson
-
Stefan Monnier
-
Taral
-
Tomasz Zielonka