Re: [Haskell-cafe] New type of ($) operator in GHC 8.0 is problematic

As Manuel wrote:
I expect that every single person teaching Haskell is going to be unhappy about it.
Indeed I am. (Will be teaching beginners next term.) - J.W.

What's changed? On 5 February 2016 at 16:14, Johannes Waldmann < johannes.waldmann@htwk-leipzig.de> wrote:
As Manuel wrote:
I expect that every single person teaching Haskell is going to be unhappy about it.
Indeed I am. (Will be teaching beginners next term.)
- J.W. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying. - J.

On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote:
What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it!

I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together. Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8: Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b Prelude> :t ($) ($) :: (a -> b) -> a -> b Beginners should only understand about following: * type variable (polymorphism) After ghc8.0: Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on). It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners. I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling. There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard. Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes. -- Kyle On Fri, Feb 5, 2016 at 8:26 AM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote:
What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I don't want, nor do I think it's a good idea, to have a beginners'
Prelude. My point about ($) was not expressly about beginners, it was about
intermediate practitioners too.
On Fri, Feb 5, 2016 at 11:55 AM, Kyle Hanson
I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners.
I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
On Fri, Feb 5, 2016 at 8:26 AM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote:
What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- Chris Allen Currently working on http://haskellbook.com

On Fri, Feb 5, 2016 at 11:29 PM, Christopher Allen
On Fri, Feb 5, 2016 at 11:55 AM, Kyle Hanson
wrote: I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners.
I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
I don't want, nor do I think it's a good idea, to have a beginners' Prelude. My point about ($) was not expressly about beginners, it was about intermediate practitioners too.
Consider these two delightful pianists: Martha https://www.youtube.com/watch?v=YLZLp6AcAi4 and Rose https://www.youtube.com/watch?v=_bjKDJD-CLc - Are they playing the same instruments? - Would they need the same teachers? - Ultimately, is the single moniker "pianist" meaningfully applicable to both? I believe we are too taken with the fact that programming language *theory* has advanced in the last couple of decades, while we miss the fact that programming *pedagogy* has regressed in the same period. And one of the big regresses is the illusion that a *single *language that spans the spectrum from beginner learning to serious software engineering is a neat idea: a grand unified/universal language. Such a language already exists -- C++. An earlier generation called it PL-1. FP in ACM Curriculum 2013 http://blog.languager.org/2015/06/functional-programming-moving-target.html spells out this – omnibus language – and such fallacies in more detail. And as regards prior art regarding the benefits for multiple close but different languages for teaching, one could see the multiple teachpacks http://docs.racket-lang.org/teachpack/index.html?q= of Scheme/Racket And even closer to home, helium http://www.open.ou.nl/bhr/heeren-helium.pdf is a haskell expressly designed to make teaching easier by not over-generalizing types

Am 09.02.2016 um 14:20 schrieb Rustom Mody:
And one of the big regresses is the illusion that a *single *language that spans the spectrum from beginner learning to serious software engineering is a neat idea: a grand unified/universal language.
It is still an ideal. Not because it is such a good idea. I'm pretty much unconvinced whether that is the case or not. It is an ideal to approximatet because learning a new language, and learning it well enough to use it in anger, is such a huge investment in time and effort, and we simply cannot afford to build a language for each domain. Also, we cannot do so because there isn't even a consensus what the domains should be, and I'd expect such a list to be a moving target anyway.
Such a language already exists -- C++. An earlier generation called it PL-1.
No no no. C++ was never meant to be easy to learn. It was intended to be easy to learn for C programmers, but C wasn't intended to be easy to learn, it was intended to be efficient on PDP-series computers with a non-optimizing compiler. PL-1 wasn't intended to be easy to learn either - it was hoped it would be, but the primary goal was to include as many language features as humanly possible, in the hopes of creating something powerful. So the two languages and the universal-easy-to-learn camp do not share any design goals.
FP in ACM Curriculum 2013 http://blog.languager.org/2015/06/functional-programming-moving-target.html spells out this – omnibus language – and such fallacies in more detail.
He claims this, but he does not back that up with any arguments. There's only reference to authority (Peter Naur).
And as regards prior art regarding the benefits for multiple close but different languages for teaching, one could see the multiple teachpacks http://docs.racket-lang.org/teachpack/index.html?q= of Scheme/Racket And even closer to home, helium http://www.open.ou.nl/bhr/heeren-helium.pdf is a haskell expressly designed to make teaching easier by not over-generalizing types
I think the link you gave is making a subtly but fundamentally different point: That to teach programming, you need a different and simplified language to get the core points across. There are actually good points to be made in favor of that approach. However, this is about making Haskell the working language easy to learn. The demography includes people who know what a type system is, what a function is, and they will usually even know what a side effect is and already avoid that if they can. Tell them that they see a simplified prelude and they will want to see the real one. Show them the real one and they will run away, flailing and scream, just as ten years ago, you could achieve the same effect by mentioning monads. If the type system is starting to make it hard to learn the language well enough to use it professionally, or to even understand what the professional library writers did and why, then the type system has become too difficult.

I'd like to opine that I personally like that our types are getting more
honest in reflecting how things work, and that even the impredicativety
hack might be on track to being a userland expressible construct in a later
ghc release (if my fuzzy understanding of the impredicative subsumption
work thats in progress is correct and that it actually pans out )
i like the bike shed, i dont care how we mix the paint colors, as long as
it doesn't create more confusion
cheers
_Carter
On Tue, Feb 9, 2016 at 9:43 AM, Joachim Durchholz
Am 09.02.2016 um 14:20 schrieb Rustom Mody:
And one of the big regresses is the illusion that a *single *language that spans the spectrum from beginner learning to serious software engineering is a neat idea: a grand unified/universal language.
It is still an ideal. Not because it is such a good idea. I'm pretty much unconvinced whether that is the case or not. It is an ideal to approximatet because learning a new language, and learning it well enough to use it in anger, is such a huge investment in time and effort, and we simply cannot afford to build a language for each domain. Also, we cannot do so because there isn't even a consensus what the domains should be, and I'd expect such a list to be a moving target anyway.
Such a language already exists -- C++.
An earlier generation called it PL-1.
No no no. C++ was never meant to be easy to learn. It was intended to be easy to learn for C programmers, but C wasn't intended to be easy to learn, it was intended to be efficient on PDP-series computers with a non-optimizing compiler. PL-1 wasn't intended to be easy to learn either - it was hoped it would be, but the primary goal was to include as many language features as humanly possible, in the hopes of creating something powerful. So the two languages and the universal-easy-to-learn camp do not share any design goals.
FP in ACM Curriculum 2013
< http://blog.languager.org/2015/06/functional-programming-moving-target.html
spells out this – omnibus language – and such fallacies in more detail.
He claims this, but he does not back that up with any arguments. There's only reference to authority (Peter Naur).
And as regards prior art regarding the benefits for multiple close but
different languages for teaching, one could see the multiple teachpacks http://docs.racket-lang.org/teachpack/index.html?q= of Scheme/Racket And even closer to home, helium http://www.open.ou.nl/bhr/heeren-helium.pdf is a haskell expressly designed to make teaching easier by not over-generalizing types
I think the link you gave is making a subtly but fundamentally different point: That to teach programming, you need a different and simplified language to get the core points across. There are actually good points to be made in favor of that approach.
However, this is about making Haskell the working language easy to learn. The demography includes people who know what a type system is, what a function is, and they will usually even know what a side effect is and already avoid that if they can. Tell them that they see a simplified prelude and they will want to see the real one. Show them the real one and they will run away, flailing and scream, just as ten years ago, you could achieve the same effect by mentioning monads.
If the type system is starting to make it hard to learn the language well enough to use it professionally, or to even understand what the professional library writers did and why, then the type system has become too difficult.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Tue, Feb 9, 2016 at 10:34 PM, Carter Schonwald < carter.schonwald@gmail.com> wrote:
I'd like to opine that I personally like that our types are getting more honest in reflecting how things work,
Me too! I just want to +1 the alternative/noob prelude suggestion which I believe came from many people; not to reduce the (default) honesty of the *standard* prelude

Am 09.02.2016 um 18:04 schrieb Carter Schonwald:
I'd like to opine that I personally like that our types are getting more honest in reflecting how things work,
I'm pretty much in the same boat with that. I just have a feeling that the typing is getting complicated because the compiler isn't good enough to infer whether a type is lifted or not, boxed or not. Essentially, there's that constant temptation to give programmers access to machine integers. It's a valid concern, but it complicates the type system tremendously. I feel that this is similar to expressing value constraints in the type system, e.g. ranges or squareness of matrixes. Yes it can be done in Haskell's type system, yes it does typecheck beautifully, but the type declarations behind these kinds of feats will just make any ordinary programmer go MEGO. Even the bright ones. I conclude that the type system isn't the right place for that kind of checking. To be understandable, such constraints need to be expressed as boolean assertions, not as some inductive construct. YMMV.

On 9 Feb 2016, at 17:17, Joachim Durchholz
wrote: I feel that this is similar to expressing value constraints in the type system, e.g. ranges or squareness of matrixes. Yes it can be done in Haskell's type system, yes it does typecheck beautifully, but the type declarations behind these kinds of feats will just make any ordinary programmer go MEGO. Even the bright ones. I conclude that the type system isn't the right place for that kind of checking. To be understandable, such constraints need to be expressed as boolean assertions, not as some inductive construct. YMMV.
+n to that, as n -> \infty Andrew Butterfield School of Computer Science & Statistics Trinity College Dublin 2, Ireland

I feel that this is similar to expressing value constraints in the type system, e.g. ranges or squareness of matrixes. Yes it can be done in Haskell's type system, yes it does typecheck beautifully, but the type declarations behind these kinds of feats will just make any ordinary programmer go MEGO. Even the bright ones. I conclude that the type system isn't the right place for that kind of checking. To be understandable, such constraints need to be expressed as boolean assertions, not as some inductive construct. YMMV
Two words: refinement types. Best regards, Marcin Mrotek

Am 09.02.2016 um 23:07 schrieb Marcin Mrotek:
I feel that this is similar to expressing value constraints in the type system, e.g. ranges or squareness of matrixes. Yes it can be done in Haskell's type system, yes it does typecheck beautifully, but the type declarations behind these kinds of feats will just make any ordinary programmer go MEGO. Even the bright ones. I conclude that the type system isn't the right place for that kind of checking. To be understandable, such constraints need to be expressed as boolean assertions, not as some inductive construct. YMMV
Two words: refinement types.
Are these in Haskell already? I see them referenced in something that's called LiquidHaskell, which has its last blog entry from Jan 2015. On http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/12/09/checking-te... , it is doing termination checks via handcrafted induction. In functions, induction (i.e. standard recursion pattern) is handled using higher-order functions, where's the higher-order logic in the predicates? And termination proofs shouldn't be implicit in the proof structure, I'd prefer a "terminates" predicate ("unlifted" if you will) on the function (not the function's type) which could be true, false, or of the form "if parameter x has properties A, B, and C, then the function is guaranteed to terminate", i.e. an implication. Just off the top of my head where I see problems for the everyday programmer. It's still interesting work. I hope somebody gets the funding to carry that to practical usefulness. (Please answer to the list, CCing the list means I can't "reply to list".)

Are these in Haskell already? I see them referenced in something that's called LiquidHaskell, which has its last blog entry from Jan 2015.
Unfortunately, I don't think they are, outside of LiquidHaskell. But LH seems to be actively developed, nevermind the stale blog - last commit to the Github repo was 11 days ago. Best regards, Marcin Mrotek

On Tue, Feb 9, 2016 at 8:13 PM, Joachim Durchholz
FP in ACM Curriculum 2013
< http://blog.languager.org/2015/06/functional-programming-moving-target.html
spells out this – omnibus language – and such fallacies in more detail.
He claims this, but he does not back that up with any arguments. There's only reference to authority (Peter Naur).
* On 23 May 2013 07:32, Malcolm Wallace wrote: *>>* -20 for generalising the Prelude *>>* +1 for removals from the Prelude *>>* -1 for adding monomorphic stuff *>>* +1000 for doing nothing *>>>>* You are all nuts. :-) *>>* I don't know if I'd go quite _that_ for as Malcolm for the weightings *>* for the different proposals... *>>* But I was speaking with a few other tutors of an introductory *>* CS/programming course that uses Haskell (note: it's teaching *>* programming with Haskell, not teaching Haskell per se: for example, *>* all pattern matchings must be done with case statements as the *>* lecturer considers top-level pattern matching a Haskell-specific *>* quirk) about these proposals...
Well if you dont like external evidence of this -- "authority" -- here's some internal evidence: An excerpt from the burning bridges exchange from the libraries list On Wed, May 22, 2013 at 8:39 PM, Ivan Lazar Miljenovic wrote: * Casey McCann responded: So in other words, your contention is that the design of the core library of Haskell should be driven by the needs of an introductory programming course, which is not even attempting to teach Haskell specifically, aimed at students who can't even figure out how tab characters work? That's marvelous. My conclusion: Casey representing library-authors and Ivan+Malcolm speaking for teachers have sufficiently divergent needs to need two different languages. Two different preludes+commandline options is a good start in that direction If you disagree what do you make of Richard Eisenberg's :
It may come as a surprise to many of you that I, too, am very worried about Haskell becoming inaccessible to newcomers. If we can't induct new people into our ranks, we will die. It is for this reason that I have always been unhappy with the FTP. But that ship has sailed.
Just as a thought experiment: The FTP had a landslide support on the libraries list. How would it have fared on a Haskell-Edu list?

I think the "show a simple type, then the 'real' type " solution is pretty good ( especially since you need to be doing type level programming to hit issues where the simple type doesn't hold anyways) , but on the topic of several languages: doesn't Haskell98 kind of solve the teaching issue? If you're using the standard stuff stick to the old $... I know Racket does some "language swapping" so that a beginner language is available . I don't know how well that works

One thing that I truly HATE about Scala API reference is that they show you the "simple" (meaning: fake) type, and you have to click to see the real type. It makes going through docs so much slower.
10.02.2016, 17:01, "Raphael Gaschignard"
I think the "show a simple type, then the 'real' type " solution is pretty good ( especially since you need to be doing type level programming to hit issues where the simple type doesn't hold anyways) , but on the topic of several languages: doesn't Haskell98 kind of solve the teaching issue? If you're using the standard stuff stick to the old $...
I know Racket does some "language swapping" so that a beginner language is available . I don't know how well that works ,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 02/10/2016 02:59 PM, Raphael Gaschignard wrote:
I think the "show a simple type, then the 'real' type " solution is pretty good ( especially since you need to be doing type level programming to hit issues where the simple type doesn't hold anyways)
It *might* work, but it needs to be *EXTREMELY* clear (visually and perhaps otherwise) in the documentation that is lying to -- ehm, I mean simplifying for -- you. Scala went with the same idea of lying (they call it the "use case") in the documentation of some of their collection method signatures and I can tell you from personal experience that it can be absolutely *infuriating* to have worked on trying to understand an error based on the documentation only to realise that what the compiler checks and what the documentation shows you isn't the same thing. Incidentally Java had a similar issue with the whole public class Foo { public static void main(String[] args) { } } incantation being needed to teach beginners the first thing about Java. You know what I did -- I just said "yeah, just ignore that bit, I'll explain when you're ready" and it wasn't actually a problem in practice. Turns out people are quite at ignoring things they don't understand as long as you *tell them* to just ignore it until later. (Also, remember that this is *one* symbol we're talking about, albeit a relatively common one. You could just *avoid* it entirely in teaching material if you so desire.) Regards,

Am 10.02.2016 um 16:32 schrieb Bardur Arantsson:
Incidentally Java had a similar issue with the whole
public class Foo { public static void main(String[] args) { } }
incantation being needed to teach beginners the first thing about Java. You know what I did -- I just said "yeah, just ignore that bit, I'll explain when you're ready" and it wasn't actually a problem in practice. Turns out people are quite at ignoring things they don't understand as long as you *tell them* to just ignore it until later.
Java's main() is a single thing that you simply copy&paste and can forget about while learning the language. Haskell's extended type declarations will reappear whenever a student explores the library. I.e. the Java issue is a single loose end. The Haskell issue keeps adding more loose ends as students progress. I am not sure how relevant that is going to be in practice. Maybe it's possible to come up with a short "for now" explanation that is consistent with what students will experience; if that's possible, I'd expect that to be much preferrable over a simplified/lying Prelude. One thing about a simplified Prelude: It will profoundly unnerve newbies. The feeling of shifting ground the first time they see that something is amiss will make them feel insecure, because they won't know how much of what they already learned will have to be thrown overboard. They won't know anymore how much they still have to learn, which is just as unnerving. If a simplified Prelude is used as a teaching tool, then that should be done in a way that allows students to look beyond that barrier, so they know which of their knowledge is preliminary, and how much they will have to learn after it.

On 02/10/2016 06:30 PM, Joachim Durchholz wrote: [--snip--]
I am not sure how relevant that is going to be in practice. Maybe it's possible to come up with a short "for now" explanation that is consistent with what students will experience; if that's possible, I'd expect that to be much preferrable over a simplified/lying Prelude.
One thing about a simplified Prelude: It will profoundly unnerve newbies. The feeling of shifting ground the first time they see that something is amiss will make them feel insecure, because they won't know how much of what they already learned will have to be thrown overboard.
How do you know that they will feel this way? I feel like most people in this thread are just going by assumptions. (I guess I could be accused of the same thing, but I guess arguing for the status quo doesn't really incur a burden of proof. We can see that it at least works somewhat well. Granted the $ type is new, but advanced type signatures abound in Haskell already. It's kind of par for the course.) Regards,

Just as a thought experiment: The FTP had a landslide support on the libraries list. How would it have fared on a Haskell-Edu list?
Judging by the list of changes at https://wiki.haskell.org/Foldable_Traversable_In_Prelude the changes were mostly some extra classes (Monoid, Foldable, Traverseable) showing up in Prelude and a bunch of type changes to functions: ... [x] ... changing to (Foldable t) => ... t x ... ... [x] ... changing to (Traverseable t) => ... t x ... This is actually quite an interesting change. Using the same names *consistently* across a wide range of types makes programs easier to write and easier to read. From an educational point of view, you can't say "We didn't need the Prelude to write `all` for us. We could have written all p (x:xs) = p x && all p xs all _ [] = True " any more because that has the wrong (old, list-specific) type. You *can* say "We could have written all = foldr True (&&) " So you lose a lesson that comes somewhere near the beginning, when you are still trying to get across the idea of higher order functions and lazy evaluation, and gain a lesson that comes much later, about the power that typeclasses add to composition. Come to think of it, you could use this to motivate typeclasses. I think you could build *just as good* an introductory Haskell course on the post-FTP libraries as you could on the pre-FTP libraries, but it would be a *different* course. The current proposal feels qualitatively different. For one thing, the FTP approach *could* have been taken back in Haskell 98, or even earlier, had someone happened to think of it, because all the typeclass machinery was there to do the job. And it would have been obviously *useful* back then: "hey, you mean that if I define a Tree type just a few more lines of code give me all these summarisation methods? Cool, this is just as good as OOP." But the current change is mainly warranted by the desire to handle unboxed types. That's an "engineering" issue: if you're trying to make Haskell go fast, even 'unknown size integers that go wrong' (Int) isn't fast enough, 'unknown size integers that violate the all-values-are-the-same-size assumption behind polymorphism' (Int#) are what you want. This is not a problem in an introductory class, where you would normally be telling people to use Integer because they have enough problems without the weirdness of fixed-size integers.

Glad to hear a teacher-pov Richard!
On Thu, Feb 11, 2016 at 5:32 AM, Richard A. O'Keefe
Just as a thought experiment: The FTP had a landslide support on the
libraries list. How would it have fared on a Haskell-Edu list?
Judging by the list of changes at https://wiki.haskell.org/Foldable_Traversable_In_Prelude the changes were mostly some extra classes (Monoid, Foldable, Traverseable) showing up in Prelude and a bunch of type changes to functions: ... [x] ... changing to (Foldable t) => ... t x ... ... [x] ... changing to (Traverseable t) => ... t x ...
This is actually quite an interesting change. Using the same names *consistently* across a wide range of types makes programs easier to write and easier to read. From an educational point of view, you can't say
"We didn't need the Prelude to write `all` for us. We could have written
all p (x:xs) = p x && all p xs all _ [] = True "
any more because that has the wrong (old, list-specific) type. You *can* say
"We could have written
all = foldr True (&&) "
So you lose a lesson that comes somewhere near the beginning, when you are still trying to get across the idea of higher order functions and lazy evaluation, and gain a lesson that comes much later, about the power that typeclasses add to composition. Come to think of it, you could use this to motivate typeclasses.
I think you could build *just as good* an introductory Haskell course on the post-FTP libraries as you could on the pre-FTP libraries, but it would be a *different* course.
I think this is a good framing of the question Lets say you take the subject matter for the introductory programming course. And you topsort it along prerequisites; ie topic A precedes topic B if understanding B needs knowledge of A So is the structure/topography of the language Haskell conformant with this topsort? Or does one need to jump against the ordering at times? As example, take a course using C to teach programming. And consider input vs pointers. You have one of 3 choices: 1. Pointers before input -- you probably know programming earlier! 2. Input before pointers -- use getchar not scanf and laboriously write atoi etc before anything else -- classic K&R 3. Bardur's solution -- "just ignore that bit (the '&') , I'll explain when you're ready" For a one-off case that's ok; when it happens at every turn teaching/learning becomes a nightmare The C version of that is described in this old paper: C in education and software engineering http://blog.languager.org/2013/02/c-in-education-and-software-engineering.ht... I just hope Haskell does not repeat that history -- especially considering that this whole discussion starts with the need to distinguish pointer-types and non-pointer types

On Thu, Feb 11, 2016 at 6:57 PM, Rustom Mody wrote:
The C version of that is described in this old paper: C in education and software engineering http://blog.languager.org/2013/02/c-in-education-and-software-engineering.ht...
Whoops pointer-indirection error :-) Sorry The original is Horrors of teaching C http://www.the-magus.in/Publications/chor.pdf The other link is a 20 year later retrospective: Yeah C was bad; what has followed is not much better

Am 12.02.2016 um 05:17 schrieb Rustom Mody:
On Thu, Feb 11, 2016 at 6:57 PM, Rustom Mody wrote:
The C version of that is described in this old paper: C in education and software engineering http://blog.languager.org/2013/02/c-in-education-and-software-engineering.ht...
Whoops pointer-indirection error :-) Sorry
Works now. Oh, and Java relevates ease of combining third-party modules, and deployment automation. The key ingredient for this was Java's mere recommendation that namespaces be based on domain names, this allowed libraries to be combined without name conflicts; the other languages don't have this, and come with horrible linking problems that only make me sad.
The original is Horrors of teaching C http://www.the-magus.in/Publications/chor.pdf The other link is a 20 year later retrospective: Yeah C was bad; what has followed is not much better
Moving horizontally means unlearning a little and learning a lot, so it is possible. It is what I did when I was forced to move from Pascal to C, and when I wanted from C to (Turbo) Pascal, then to Eiffel, in the end to Java, while studying other languages left and right - Prolog, SML, Haskell, Lisp, Alice (no particular order, not even chronologically). Of course that's just my personal learning experience, teachers will need to draw their own conclusions. Oh, and OO is inefficient. It has really bad cache locality. Game programmers move towards "entity systems", redistributing object attributes into arrays which are held together by an ID. You get the in-memory equivalent of a relational database with a star schema (and user-defined datatypes in columns, so it's a bit less primitive than one might think). Sorry for spinning off-topic, if there's no interest I'll take further discussion to private mail.

On Fri, Feb 12, 2016 at 2:25 PM, Joachim Durchholz
Am 12.02.2016 um 05:17 schrieb Rustom Mody:
On Thu, Feb 11, 2016 at 6:57 PM, Rustom Mody wrote:
The C version of that is described in this old paper: C in education and software engineering < http://blog.languager.org/2013/02/c-in-education-and-software-engineering.ht...
Whoops pointer-indirection error :-) Sorry
Works now.
Oh, and Java relevates ease of combining third-party modules, and deployment automation. The key ingredient for this was Java's mere recommendation that namespaces be based on domain names, this allowed libraries to be combined without name conflicts; the other languages don't have this, and come with horrible linking problems that only make me sad.
Thanks for the input I would have thought that SML would be the one which had the most sophisticated module-sublanguage. Would be interested to know how SML and Java stack up against each other in that respect.

Am 13.02.2016 um 07:11 schrieb Rustom Mody:
I would have thought that SML would be the one which had the most sophisticated module-sublanguage. Would be interested to know how SML and Java stack up against each other in that respect.
I never understood SML's module system. The explanations I found were focused on the "what", and very intricate, but I never found an explanation "why" they were doing it. My impression was that it was quite sophisticated in its possibilities to adapt a module during import, but I was never sure whether SML's notion of module was even similar to that in other languages. The Java module system isn't spectactular, essentially an import establishes visibility and nothing more (adaptation is separate, and limited to type parameters), and you have a hierarchical namespace. The only thing that sets Java apart is that the DNS namespace is used as the basis, and that's not even a language rule, just a recommendation; the fascinating thing is that a mere recommendation was enough to make clear who's responsible for fixing a name conflict, and virtually eliminate name conflicts from the Java world. [Please don't mail directly and CC to Haskell-cafe, this defeats my mailer's "reply to list" function.]

I never understood SML's module system. [...] but I never found an explanation "why" they were doing it.
http://stackoverflow.com/questions/23006951/encoding-standard-ml-modules-in-... http://homepages.inf.ed.ac.uk/mfourman/teaching/mlCourse/notes/sml-modules.h... https://existentialtype.wordpress.com/2011/04/16/modules-matter-most/ https://www.reddit.com/r/haskell/comments/2foggq/why_does_david_turner_say_t... https://www.reddit.com/r/haskell/comments/2foggq/why_does_david_turner_say_t... Some quotes from http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf - ML Modules and Haskell Type Classes: A Constructive Comparison 5.1
ML modules provide proper namespace management, whereas Haskell type classes do not: It is not possible that two different type classes (in the same Haskell module) declare members of the same name.
Signatures and structures in ML may contain all sorts of language constructs, including substructures. Type classes and instances in Haskell 98 may contain only methods; extensions to Haskell 98 also allow type synonyms [6] and data types [5]. However, there exists no extension that allows nested type classes and instances. (Ed. untrue save for the last bit given extent of use of GHC extensions now.)
Signatures in ML are essentially anonymous because named signatures can be removed from the language without losing expressiveness. Haskell type classes cannot be anonymous.
In ML, matching a structure against a signature is performed by comparing the structure and the signature componentwise; the names of the structure and the signature—if present at all—do not matter. This sort of signature matching is often called structural matching. Our Haskell analog of signature matching is verifying whether the type representing a structure is an instance of the type class representing the signature. The name of a class is crucial for this decision. Therefore, we characterize our Haskell analog of signature matching as nominal.
In ML, abstraction is performed by sealing a structure with a translucent or opaque signature. In Haskell, we perform abstraction inside instance declarations through abstract associated type synonyms. ( http://research.microsoft.com/en-us/um/people/simonpj/papers/assoc-types/at-... <--- probably mean this paper but I don't totally get how this brings about abstraction)
A sealed structure in ML may look different depending on whether we view its body from inside or outside the signature seal: Inside, more values and types may be visible, some types may be concrete, and some values may have a more polymorphic type than outside. For our Haskell analog, the same set of types and values is visible and a value has the same type, regardless of whether we view the instance from inside or outside. (Hand wobble)
5.2
Overloading in Haskell is resolved implicitly by the compiler. When type classes are simulated with ML modules, overloading has to be resolved explicitly by the programmer, which leads to awkward and verbose code.
Our current translation scheme is unable to handle constructor classes because there is not direct counterpart of Haskell’s higher-oder types in ML. We consider it as interesting future work to investigate whether an encoding of higher-order types as functors would enable a translation of constructor classes to ML modules.
Type classes in Haskell may be recursive in the sense that a class can be used in a constraint for a method of the same class. We cannot translate such recursive classes to ML because signatures cannot be recursive.
Haskell type classes may contain default definitions for methods. With our approach, such default definitions cannot be translated properly to ML because signatures specify only the types of value components and cannot contain implementations of value components.
---- My two cents: focusing on different priorities WRT "abstraction". Typeclasses started out focusing on ad-hoc polymorphism and making that convenient. ML modules started out focusing on abstraction and modularity. They've both (GHC extensions, applicative module functors) been plucking their way into their respective local maxima in terms of what they can do for both.
From http://www-plan.cs.colorado.edu/diwan/class-papers/ML-doc.pdf in 3.1 "The Modules System" by Harper, the emphasis in the opening paragraph is educative in where the priorities are:
The ability to decompose a large program into a collection of relatively independent modules with well-de fined interfaces is essential to the task of building and maintaining large programs. The ML modules system supplements the core language with constructs to facilitate building and maintaining large programs.
ML's conception of a program unit is that it is a reified environment.
Now the fundamental notion underlying program modularization is that the aim is to partition the environment into chunks that can be manipulated relatively independently of one another.
This is not how people think about, talk about, or motivate typeclasses in
my experience. I haven't run into anyone fool-hardy enough to suggest a
global namespace of unique instances getting provided by the compiler is a
vehicle for modularity-via-(abstraction|sealing|etc.) YMMV
HTH
On Sat, Feb 13, 2016 at 1:03 AM, Joachim Durchholz
Am 13.02.2016 um 07:11 schrieb Rustom Mody:
I would have thought that SML would be the one which had the most sophisticated module-sublanguage. Would be interested to know how SML and Java stack up against each other in that respect.
I never understood SML's module system. The explanations I found were focused on the "what", and very intricate, but I never found an explanation "why" they were doing it. My impression was that it was quite sophisticated in its possibilities to adapt a module during import, but I was never sure whether SML's notion of module was even similar to that in other languages.
The Java module system isn't spectactular, essentially an import establishes visibility and nothing more (adaptation is separate, and limited to type parameters), and you have a hierarchical namespace. The only thing that sets Java apart is that the DNS namespace is used as the basis, and that's not even a language rule, just a recommendation; the fascinating thing is that a mere recommendation was enough to make clear who's responsible for fixing a name conflict, and virtually eliminate name conflicts from the Java world.
[Please don't mail directly and CC to Haskell-cafe, this defeats my mailer's "reply to list" function.]
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- Chris Allen Currently working on http://haskellbook.com

Am 13.02.2016 um 08:31 schrieb Christopher Allen:
This is not how people think about, talk about, or motivate typeclasses in my experience. I haven't run into anyone fool-hardy enough to suggest a global namespace of unique instances getting provided by the compiler is a vehicle for modularity-via-(abstraction|sealing|etc.) YMMV
Well, make me the first one to do that ;-) Not for typeclasses. I suspect that namespaces and visibility should be managed independently of adaptation to the local needs, and I also suspect that both typeclasses and SML modules try to do both, making it hard to understand these aspects in isolation. The quoted paragraphs were a comparison. It seems that the SML folks are about namespace collisions of stuff from within a module. If that's a problem in Haskell, then that's an emarrassing weakness. If the SML folks think that's already "proper namespace management", they are mistaken (sorry folks), you need to deal with cases where module names themselves conflict. It's easy to underestimate the effect. For me, everything I code and vaguely expect to ever become public, I simply put into the org.durchholz namespace. That's my personal DNS name, so whenever I get around to actually publish something, at least I don't have to worry about having to rename uses lay_golden_eggs module because Goose Inc. already took that particular place in the global namespace. Even better, I don't have to worry about contacting everybody who uses my code to update it with the new name. [Please don't answer directly and CC to Haskell-Cafe, that's defeating my mailer's "reply-to-list" feature.]

On 02/13/2016 08:03 AM, Joachim Durchholz wrote:
Am 13.02.2016 um 07:11 schrieb Rustom Mody:
I would have thought that SML would be the one which had the most sophisticated module-sublanguage. Would be interested to know how SML and Java stack up against each other in that respect.
I never understood SML's module system. The explanations I found were focused on the "what", and very intricate, but I never found an explanation "why" they were doing it. My impression was that it was quite sophisticated in its possibilities to adapt a module during import, but I was never sure whether SML's notion of module was even similar to that in other languages.
It's _hugely_ more advanced than most module systems out there. AFAIUI O'Caml has a similar system, but I'm not sure it's quite that advanced. Basically it _can_ subsume type classes, but using it for that purpose is unbelievably verbose and it doesn't get you global coherence. They also allow you to *cleanly* separate interface from implementation. (S/O'Ca)ML modules are the one thing I miss about programming in O'Caml relative to Haskell. (Alright, there are a couple of other minor things, but we're probably off-topic enough already :))
The Java module system isn't spectactular, essentially an import establishes visibility and nothing more (adaptation is separate, and limited to type parameters), and you have a hierarchical namespace.
It's terrible and basically name-space only. What's even stranger is that it's an *open* namespace (apart from the standard ones, I believe?) which means that I can retroactively add a class in say, org.spring.integration and access package-scope bits of the original org.spring.integration namespace. It's a pretty weird system that seems to be driven by Java's "one-class-per-file" mentality[1]. Disclaimer: Java 9 is getting a new module system, Jigsaw, but I'm not really too familiar with what it actually does, so I won't comment beyond this disclaimer :).
The only thing that sets Java apart is that the DNS namespace is used as the basis, and that's not even a language rule, just a recommendation; the fascinating thing is that a mere recommendation was enough to make clear who's responsible for fixing a name conflict, and virtually eliminate name conflicts from the Java world.
Yes, *this* was a FANTASTIC idea and it's soooo simple once you're aware of it. (No sarcasm intended, btw.) Unfortunately, the Scala people seem to be tiring of the long names and are regressing towards just using top-level names like "play" and "argonaut" and such... :/
[Please don't mail directly and CC to Haskell-cafe, this defeats my mailer's "reply to list" function.]
Apologies if I'm doing this. I'm using the list through GMANE and cannot help it. [1] Yes, I'm aware that you can actually have multiple classes in a file. Almost nobody does that. Regards,

On Sat, Feb 13, 2016 at 5:11 AM, Bardur Arantsson
Basically it _can_ subsume type classes, but using it for that purpose is unbelievably verbose and it doesn't get you global coherence.
If it doesn’t get you global coherence, then it does not subsume type classes, as that is most of the reason type classes are useful.

On 02/13/2016 11:39 AM, Manuel Gómez wrote:
On Sat, Feb 13, 2016 at 5:11 AM, Bardur Arantsson
wrote: Basically it _can_ subsume type classes, but using it for that purpose is unbelievably verbose and it doesn't get you global coherence.
If it doesn’t get you global coherence, then it does not subsume type classes, as that is most of the reason type classes are useful.
That's a fair observation, but I meant subsume in a slightly different way, i.e. you can do everything with ML modules that you could possibly do with type classes... and more! But of course we're talking Turing Complete languages so that may be a moot point in terms of "power" wrt. computability (etc.), but it sure isn't in terms of daily practice! ML modules are truly one of the features of O'Caml that I miss most... still I'm using Haskell in preference to O'Caml now, so it can't be _that_ bad, right? :) Btw, technically even Haskell-as-implemented-by-GHC doesn't give you coherent instances because -XIncoherentInstances (and maybe OverlappingInstances?). I know that's _slightly_ unfair, but if we're arguing techincalities... Regards,

Am 13.02.2016 um 10:41 schrieb Bardur Arantsson:
It's terrible and basically name-space only.
That's just "do one thing, and do it well". Or, conversely, "just the namespace, look elsewhere for what you think a module system is". I guess the worst problem with SML's module system is that it does so many thing all rolled into one.
What's even stranger is that it's an *open* namespace (apart from the standard ones, I believe?) which means that I can retroactively add a class in say,
org.spring.integration
and access package-scope bits of the original org.spring.integration namespace.
Yes, but you get into all sorts of trouble when packaging, and all kinds of tools will detect that and your software will be seen as low-quality. Also, you can't do this unintentionally, so it's not a source of bugs. So while possible in theory, it does not happen in practice unless in the most dire circumstances, and even then it's not the workaround people choose because it's too painful.
It's a pretty weird system that seems to be driven by Java's "one-class-per-file" mentality[1].
Nah, that's unrelated.
Disclaimer: Java 9 is getting a new module system, Jigsaw, but I'm not really too familiar with what it actually does, so I won't comment beyond this disclaimer :).
Jigsaw is about the ability to define large-scale modules. I doubt it's a good approach, but I guess I'll work with it and see whether that's really going to be the case. The Java culture isn't so much about good theory but about good engineering, so it's quite possible that my fears are unfounded.
The only thing that sets Java apart is that the DNS namespace is used as the basis, and that's not even a language rule, just a recommendation; the fascinating thing is that a mere recommendation was enough to make clear who's responsible for fixing a name conflict, and virtually eliminate name conflicts from the Java world.
Yes, *this* was a FANTASTIC idea and it's soooo simple once you're aware of it. (No sarcasm intended, btw.)
Agreed, but if you were designing a language in the pre-Java times, the DNS was not the global registry where every programming-related entity would have a globally unique name. Most companies didn't even know what a domain name is, let alone have it registered. Java went public roughly at the time that the Internet^W^W HTTP took off, and I suppose the idea to use domain names as the global namespace was more of a lucky accident because the plan was to make Java the Lingua Franca of browser scripting, and they *had* to have a way to make all those dynamically-loaded modules coexist, and in the WWW, the domain names come naturally. Had Java been planned to become the server language it is today, I doubt they'd have had that idea.
Unfortunately, the Scala people seem to be tiring of the long names and are regressing towards just using top-level names like "play" and "argonaut" and such... :/
I think that's mostly exceptions for the really-well-known frameworks. Similar to the "java.*" and "javax.*" namespaces. I can understand why they prefer "play.*" over "com.playframework.*", but I don't really get why they say "argonaut.*" instead of "io.argonaut.*". Weird. OT3H it's not a really serious problem. The DNS as namespace means you have a spot that's guaranteed to be free for your code, and as long as nobody uses a TLD for his package root, it's all fine. Things could become ugly for the Play framework is somebody registers "play" as a new TLD. It would be a clearly a problem for the Play framework, not for the DNS, so at least the responsibilities for fixing the problem will be easy to assign. Or maybe the Playframework guys will get a chance at reserving whatever .play domains would collide with their package names, it's quite possible they'd get heard during the sunrise period of a hypothetical new .play TLD. So... things will muddle through as usual, and the main benefit is that you can choose a DNS-based spot in the namespace and be guaranteed that nobody else can usurp it because you registered that domain. And if somebody publishes code in that part of the namespace anyway, they will get labelled as "misleading" or even "abusive" and will be forced to move elsewhere.
[1] Yes, I'm aware that you can actually have multiple classes in a file. Almost nobody does that.
It's mostly useless because you cannot have multiple public classes in a file, the other classes need to be package-private. And almost nobody uses package-private because the visibility rules around that are complicated, i.e. it is almost never what you want and also bad for maintenance.

(This is getting waaaaayyy off-topic, so I'll stop after this post.) On 02/13/2016 12:31 PM, Joachim Durchholz wrote:
Unfortunately, the Scala people seem to be tiring of the long names and are regressing towards just using top-level names like "play" and "argonaut" and such... :/
I think that's mostly exceptions for the really-well-known frameworks. Similar to the "java.*" and "javax.*" namespaces. I can understand why they prefer "play.*" over "com.playframework.*", but I don't really get why they say "argonaut.*" instead of "io.argonaut.*". Weird.
Unfortunately it's not only famous/popular frameworks... but upon reconsideration, actually, I think the problem may be imports being "scoped" in a good-in-theory-but-bad-in-practice way. The issue usually is an inability to refer to things with short names because you have clashing imports. (I won't expand in the interest of brevity. This is off-topic enough already :).)
OT3H it's not a really serious problem. The DNS as namespace means you have a spot that's guaranteed to be free for your code, and as long as nobody uses a TLD for his package root, it's all fine. Things could become ugly for the Play framework is somebody registers "play" as a new TLD. It would be a clearly a problem for the Play framework, not for the DNS, so at least the responsibilities for fixing the problem will be easy to assign. Or maybe the Playframework guys will get a chance at reserving whatever .play domains would collide with their package names, it's quite possible they'd get heard during the sunrise period of a hypothetical new .play TLD.
Well, reserving a name is kind of precondition, but *expiry* of domains could also be bad -- arguably *worse*. I think the main benefit isn't really tied to domain names per se, but that fact that people/companies *tend to* pick names that are slightly different as top-level/next-level/next-level so conflicts would be rare *in practice* even if there wasn't a "have-a-domain-name" requirement -- which in fact there *isn't*; it's still just a convention in Java, but it *is* pervasively followed. Regards,

I would propose instead that two types are presented. A simple and complex
type. The complex type being the most polymorphic and the simple type being
the easiest to teach and explain while still not losing the concept of the
operator. This way you can present what the real complex type is while also
showing what it can be reduced to essentially for our current purposes
To those saying this would turn others off Haskell I disagree because it is
similar to how we treat functions like black boxes and only identify them
by type, name, and documentation. For the purposes of a beginning student
they do not need to know the full type system, and thus should treat it as
a black box beyond the parts which they are learning. In time one comes to
understand the type system but one does not need to understand it at first.
On Tue, Feb 9, 2016 at 7:20 AM, Rustom Mody
On Fri, Feb 5, 2016 at 11:29 PM, Christopher Allen
wrote: On Fri, Feb 5, 2016 at 11:55 AM, Kyle Hanson
wrote: I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners.
I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
I don't want, nor do I think it's a good idea, to have a beginners' Prelude. My point about ($) was not expressly about beginners, it was about intermediate practitioners too.
Consider these two delightful pianists: Martha https://www.youtube.com/watch?v=YLZLp6AcAi4 and Rose https://www.youtube.com/watch?v=_bjKDJD-CLc
- Are they playing the same instruments? - Would they need the same teachers? - Ultimately, is the single moniker "pianist" meaningfully applicable to both?
I believe we are too taken with the fact that programming language *theory* has advanced in the last couple of decades, while we miss the fact that programming *pedagogy* has regressed in the same period. And one of the big regresses is the illusion that a *single *language that spans the spectrum from beginner learning to serious software engineering is a neat idea: a grand unified/universal language. Such a language already exists -- C++. An earlier generation called it PL-1.
FP in ACM Curriculum 2013 http://blog.languager.org/2015/06/functional-programming-moving-target.html spells out this – omnibus language – and such fallacies in more detail.
And as regards prior art regarding the benefits for multiple close but different languages for teaching, one could see the multiple teachpacks http://docs.racket-lang.org/teachpack/index.html?q= of Scheme/Racket And even closer to home, helium http://www.open.ou.nl/bhr/heeren-helium.pdf is a haskell expressly designed to make teaching easier by not over-generalizing types
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Perhaps it will aid the discussion to see that the type of ($) will, for better or worse, be changing again before 8.0. The problem is described in GHC ticket #11471. The details of "why" aren't all that important for this discussion, but the resolution might be. The new (hopefully final!) type of ($) will be:
($) :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). (a -> b) -> a -> b
Once again, it's easy enough to tweak the pretty-printer to hide the complexity. But perhaps it's not necessary. The difference as far as this conversation is concerned is that Levity has been renamed to RuntimeRep. I think this is an improvement, because now it's not terribly hard to explain:
---
1. Types of kind * have values represented by pointers. This is the vast majority of data in Haskell, because almost everything in Haskell is boxed.
2. But sometimes, we don't care how a value is represented. In this case, we can be polymorphic in the choice of representation, just like `length` is polymorphic in the choice of list element type.
3. ($) works with functions whose result can have any representation, as succinctly stated in the type. Note that the argument to the function must be boxed, however, because the implementation of ($) must store and pass the argument. It doesn't care at all about the result, though, allowing for representation-polymorphism.
In aid of this explanation, we can relate this all to Java. The reference types in Java (e.g., Object, int[], Boolean) are all like types of kind *. The primitive types in Java (int, boolean, char) do not have kind *. Java allows type abstraction (that is, generics) only over the types of kind *. Haskell is more general, allowing abstraction over primitive types via representation polymorphism.
---
Could this all be explained to a novice programmer? That would be a struggle. But it could indeed be explained to an intermediate programmer in another language just learning Haskell.
For point of comparison, Java is widely used as a teaching language. And yet one of the simplest programs is
public class HelloWorld
{
public static void main(String[] args)
{
System.out.println("Hello, world!");
}
}
When I taught Java (I taught high-school full time for 8 years), I would start with something similar to this and have to tell everyone to ignore 90% of what was written. My course never even got to arrays and `static`! That was painful, but everyone survived. This is just to point out that Haskell isn't the only language with this problem. Not to say we shouldn't try to improve!
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently. The old type for ($) was just a plain old lie. Now, at least, we're not lying. So, do we 1) lie, 2) allow the language to grow, or 3) avoid certain growth because it affects how easy the language is to learn? I don't really think anyone is advocating for (3) exactly, but it's hard to have (2) and not make things more complicated -- unless we have a beginners' mode or other features in, say, GHCi that aid learning. As I've said, I'm in full favor of adding these features.
Richard
On Feb 5, 2016, at 12:55 PM, Kyle Hanson
I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others: From Takenobu Tani Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From Richard Eisenberg: - It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners. I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
On Fri, Feb 5, 2016 at 8:26 AM, Tom Ellis
wrote: On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote: What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Changing the name doesn't fix the issue. The issue is the noise and the referent, not the referrer. There's a habit of over-focusing on names in programming communities. I think it'd be a mistake to do that here and risk missing the point. You can make all of the keywords in the Java example salient early on, but you cannot make the implementation details you're exposing in the type of ($) relevant unless they already have a year or two of Haskell under their belts. Listing out the keywords: 1. public 2. class 3. (class name) 4. static 5. void 6. (method name) 7. (method arguments) Explaining public, class, static, and void usually happens pretty soon after the basics in a Java course. Importantly, they're things you _need_ to know to get things done properly in Java. The same is not true of what is mentioned in the type of ($). The implicit prenex form and forall are irrelevant for learners until they get to Rank2/RankN which is very much beyond, "I am learning Haskell" and into, "I am designing an API in Haskell for other people to use". * vs. # is something many working and hobbyist Haskellers I've known will scarcely know anything about. There is a big difference, to my mind, between what is being exposed here in Java versus what is being exposed in the type ($). Consider that the boxed/unboxed distinction exists in Java but needn't come up in any beginner tutorials.
Types of kind * have values represented by pointers. This is the vast majority of data in Haskell, because almost everything in Haskell is boxed.
We can't assume Haskell learners know what pointers are. This, again,
creates unnecessary noise for learners by forcing exposure to things that
are irrelevant for a very long time.
On Fri, Feb 5, 2016 at 12:13 PM, Richard Eisenberg
Perhaps it will aid the discussion to see that the type of ($) will, for better or worse, be changing again before 8.0.
The problem is described in GHC ticket #11471. The details of "why" aren't all that important for this discussion, but the resolution might be. The new (hopefully final!) type of ($) will be:
($) :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). (a -> b) -> a -> b
Once again, it's easy enough to tweak the pretty-printer to hide the complexity. But perhaps it's not necessary. The difference as far as this conversation is concerned is that Levity has been renamed to RuntimeRep. I think this is an improvement, because now it's not terribly hard to explain:
--- 1. Types of kind * have values represented by pointers. This is the vast majority of data in Haskell, because almost everything in Haskell is boxed. 2. But sometimes, we don't care how a value is represented. In this case, we can be polymorphic in the choice of representation, just like `length` is polymorphic in the choice of list element type. 3. ($) works with functions whose result can have any representation, as succinctly stated in the type. Note that the argument to the function must be boxed, however, because the implementation of ($) must store and pass the argument. It doesn't care at all about the result, though, allowing for representation-polymorphism.
In aid of this explanation, we can relate this all to Java. The reference types in Java (e.g., Object, int[], Boolean) are all like types of kind *. The primitive types in Java (int, boolean, char) do not have kind *. Java allows type abstraction (that is, generics) only over the types of kind *. Haskell is more general, allowing abstraction over primitive types via representation polymorphism. ---
Could this all be explained to a novice programmer? That would be a struggle. But it could indeed be explained to an intermediate programmer in another language just learning Haskell.
For point of comparison, Java is widely used as a teaching language. And yet one of the simplest programs is
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } }
When I taught Java (I taught high-school full time for 8 years), I would start with something similar to this and have to tell everyone to ignore 90% of what was written. My course never even got to arrays and `static`! That was painful, but everyone survived. This is just to point out that Haskell isn't the only language with this problem. Not to say we shouldn't try to improve!
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently. The old type for ($) was just a plain old lie. Now, at least, we're not lying. So, do we 1) lie, 2) allow the language to grow, or 3) avoid certain growth because it affects how easy the language is to learn? I don't really think anyone is advocating for (3) exactly, but it's hard to have (2) and not make things more complicated -- unless we have a beginners' mode or other features in, say, GHCi that aid learning. As I've said, I'm in full favor of adding these features.
Richard
On Feb 5, 2016, at 12:55 PM, Kyle Hanson
wrote: I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners.
I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
On Fri, Feb 5, 2016 at 8:26 AM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote:
What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
-- Chris Allen Currently working on http://haskellbook.com

On 02/05/2016 08:05 PM, Christopher Allen wrote:
Changing the name doesn't fix the issue. The issue is the noise and the referent, not the referrer. There's a habit of over-focusing on names in programming communities. I think it'd be a mistake to do that here and risk missing the point.
I think you're being a bit harsh, but I *do* think you're essentially right. Beginners will have no idea what most the that means, so... *yes* the type *will* need to be simplified for display purposes. (Unless, of course, you opt-in to full signatures.) Regards,

I just showed the type of ($) to my boss in our company chat who has been using Haskell for 14 years. He'd played with Haskell prior to that, but 14 years ago is when he started postgrad and teaching Haskell. Here's what he said:
...what? what does that do?
He's been using Haskell in production for the last 5 years as well, I think.
Please simplify the type unless a pragma specific to levity is turned on.
As it happens, I like the name levity better than runtimerep, but neither
solve any pedagogical issues. YMMV.
On Fri, Feb 5, 2016 at 1:12 PM, Bardur Arantsson
On 02/05/2016 08:05 PM, Christopher Allen wrote:
Changing the name doesn't fix the issue. The issue is the noise and the referent, not the referrer. There's a habit of over-focusing on names in programming communities. I think it'd be a mistake to do that here and risk missing the point.
I think you're being a bit harsh, but I *do* think you're essentially right. Beginners will have no idea what most the that means, so... *yes* the type *will* need to be simplified for display purposes. (Unless, of course, you opt-in to full signatures.)
Regards,
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- Chris Allen Currently working on http://haskellbook.com

Why must ($) be kind-polymorphic? It seems as though there is a small enough base of unboxed code that having e.g. ($#) would be fine. If that won't work, would it be possible to have something like ($) :: forall k a (b :: k) . (a -> b) -> a -> b I don't know if this is possible in Haskell now, but I believe the currently popular dependently typed languages allow this sort of thing.
($) :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). (a -> b) -> a -> b

On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?

On Fri, Feb 05, 2016 at 07:19:25PM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?
Does anyone have any idea about this? What is it about ($) that means it needs a new funky type whereas (apparently) nothing else does?

On Sat, Feb 06, 2016 at 11:59:20AM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 07:19:25PM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?
Does anyone have any idea about this? What is it about ($) that means it needs a new funky type whereas (apparently) nothing else does?
For example, why should maybe not be extended from maybe :: b -> (a -> b) -> Maybe a -> b to maybe :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). b -> (a -> b) -> Maybe a -> b That's strictly more general, is it not? Tom

maybe :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). b -> (a -> b) -> Maybe a -> b
`b` also is a type of an argument in that function, so I think being levity polymorphic on it in is excluded by "GC going haywire chasing values as if they were pointers" as described here: https://mail.haskell.org/pipermail/ghc-devs/2016-February/011269.html Best regards, Marcin Mrotek

On Sat, Feb 06, 2016 at 01:31:14PM +0100, Marcin Mrotek wrote:
maybe :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). b -> (a -> b) -> Maybe a -> b
`b` also is a type of an argument in that function, so I think being levity polymorphic on it in is excluded by "GC going haywire chasing values as if they were pointers" as described here: https://mail.haskell.org/pipermail/ghc-devs/2016-February/011269.html
Ah, "The levity polymorphic type never appears directly to the left of an arrow.". Thanks.

out of curiosity, what are * and #? I tried to search but did not find. Thank you

actually "#" worked on Hoogle: https://wiki.haskell.org/Keywords#.23

Hi,
The '*' means kind "lifted".
The '#' means kind "unlifted".
Futhermore,
"TYPE 'Lifted" is alias to '*'
"TYPE 'Unlifted" is alias to '#'
Are these also useful?
[1] https://wiki.haskell.org/Kind
[2] https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TypeType
[3] https://ghc.haskell.org/trac/ghc/wiki/UnliftedDataTypes
[4]
https://takenobu-hs.github.io/downloads/haskell_lazy_evaluation.pdf#page=192
Regards,
Takenobu
2016-02-06 21:48 GMT+09:00 Imants Cekusins
actually "#" worked on Hoogle:
https://wiki.haskell.org/Keywords#.23 _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Thank you Takenobu the links are useful, yes. Is knowledge of these terms necessary to program or are these terms of most interest to compiler developers?

Hi Imants and cafe,
Is knowledge of these terms necessary to program or are these terms of most interest to compiler developers?
Ah, I think not necessary, but useful for abstraction and optimization of
program :)
I share something about Kind, here :
[1] Learn You a Haskell for Great Good!, Kinds and some type-foo
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#kinds-and-s...
[2] Haskell 2010 Language Report, 4.1.1 Kinds
https://www.haskell.org/definition/haskell2010.pdf
[3]
http://takenobu-hs.github.io/downloads/type_introduction_illustrated.pdf#pag...
Regards,
Takenobu
2016-02-06 22:15 GMT+09:00 Imants Cekusins
Thank you Takenobu
the links are useful, yes.
Is knowledge of these terms necessary to program or are these terms of most interest to compiler developers? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

As you dig deeper into Haskell you'll eventually need to understand what
these mean to reason about anything beyond first order code.
The primitives that GHC uses to implement arrays, references and the like
live in #. We then wrap them in something in * before exposing them to the
user, but you can shave a level of indirection by knowing what lives in #
and what doesn't.
But even if you never care about #, Int, Double, etc. are of kind *,
Functors are of kind * -> *, etc. so to talk about the type of types at all
you need to be able to talk about these concepts at all with any rigor, and
to understand why Maybe Maybe isn't a thing.
On Sat, Feb 6, 2016 at 8:15 AM, Imants Cekusins
Thank you Takenobu
the links are useful, yes.
Is knowledge of these terms necessary to program or are these terms of most interest to compiler developers? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sat, Feb 06, 2016 at 12:12:47PM -0500, Edward Kmett wrote:
As you dig deeper into Haskell you'll eventually need to understand what these mean to reason about anything beyond first order code.
The primitives that GHC uses to implement arrays, references and the like live in #. We then wrap them in something in * before exposing them to the user, but you can shave a level of indirection by knowing what lives in # and what doesn't.
But even if you never care about #, Int, Double, etc. are of kind *, Functors are of kind * -> *, etc. so to talk about the type of types at all you need to be able to talk about these concepts at all with any rigor, and to understand why Maybe Maybe isn't a thing.
I think this is a bit pessimistic. I've been a professional Haskell developer for some years without ever needing to know what # is. I'm pretty sure that a professional Haskeller could be comfortable only know about * on a vague, intuitive level. Tom

On Sat, Feb 6, 2016 at 12:42 PM, Edward Kmett
The primitives that GHC uses to implement arrays, references and the like live in #. We then wrap them in something in * before exposing them to the user, but you can shave a level of indirection by knowing what lives in # and what doesn't.
Yes! Let’s not forget, of course, that these (or similar) have been in GHC for many, many years, right in the Prelude: ```
:i Int Char Float Double IO Integer data Int = GHC.Types.I# GHC.Prim.Int# data Char = GHC.Types.C# GHC.Prim.Char# data Float = GHC.Types.F# GHC.Prim.Float# data Double = GHC.Types.D# GHC.Prim.Double# newtype IO a = GHC.Types.IO (GHC.Prim.State# GHC.Prim.RealWorld -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #)) data Integer = integer-gmp-1.0.0.0:GHC.Integer.Type.S# !GHC.Prim.Int# | integer-gmp-1.0.0.0:GHC.Integer.Type.Jp# {-# UNPACK #-}integer-gmp-1.0.0.0:GHC.Integer.Type.BigNat | integer-gmp-1.0.0.0:GHC.Integer.Type.Jn# {-# UNPACK #-}integer-gmp-1.0.0.0:GHC.Integer.Type.BigNat
Stepping outside the Prelude, yet well within beginner territory,
brings even more fun:
:i Map Set data Map k a = containers-0.5.6.2:Data.Map.Base.Bin {-# UNPACK #-}containers-0.5.6.2:Data.Map.Base.Size !k a !(Map k a) !(Map k a) | containers-0.5.6.2:Data.Map.Base.Tip data Set a = containers-0.5.6.2:Data.Set.Base.Bin {-# UNPACK #-}containers-0.5.6.2:Data.Set.Base.Size !a !(Set a) !(Set a) | containers-0.5.6.2:Data.Set.Base.Tip
Unboxed types, the UNPACK pragma, references to GHC.Prim (which easily
lead to confusing exploration), unboxed tuples, an unboxed State
monad, RealWorld, bang patterns, unexported constructors,
implementation details for abstract types… all of them available right
from the prompt of the Prelude using the main tool for exploratory
learning that beginners rely on.
I’m not saying this is a good thing and I’m not saying this should be
fixed. I’m not even saying this is comparable to the situation with $
and I’m likewise not saying presenting these concepts to beginners
should be thought of as comparable to presenting levity polymorphism
to beginners. It is nonetheless relevant context to this discussion;
the Prelude has always had concepts unfriendly to beginners readily
available, and Haskell beginner teachers have always had to work
around these issues. Students have always asked about these things.
> But even if you never care about #, Int, Double, etc. are of kind *,
> Functors are of kind * -> *, etc. so to talk about the type of types at all
> you need to be able to talk about these concepts at all with any rigor, and
> to understand why Maybe Maybe isn't a thing.
In my personal teaching experience, it is extremely helpful to discuss
kinds in the first introduction of type constructors, after covering
types with no parameters. This is especially helpful in discussing
how the hierarchy leading to Monad works, and why things like
«instance Functor (Tree Int) where …» don’t make sense and why
«instance Functor Tree where …» must be parametric in the type of the
thing in the tree, which in turn motivates a lot more discussion.
Teaching kinds is teaching Haskell basics. It is not an advanced
topic. It ought to be covered near the very first lessons on Haskell
for absolute beginners.

By way of a counterpoint to the "showing complicated things alienates
beginners" argument, remember that to a beginner there are already very
many things on the screen that they won't (and needn't immediately)
understand. For instance, this is what a simple `stack ghci` in my home
directory says to me:
$ stack ghci
Run from outside a project, using implicit global project config
Using resolver: lts-2.22 from implicit global project's config file:
/home/linuxadmin/.stack/global/stack.yaml
Error parsing targets: The specified targets matched no packages.
Perhaps you need to run 'stack init'?
Warning: build failed, but optimistically launching GHCi anyway
Configuring GHCi with the following packages:
GHCi, version 7.8.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Ok, modules loaded: none.
Prelude>
There's a lot of stuff there you don't need as a beginner. The line
beginning 'Error' is a bit scary, as is the 'Warning'. The advice to run
'stack init' is not good advice. The advice to use :? for help is probably
the most beginner-useful thing in all that and it looks like line noise
rather than a thing you might want to actually type!
My point is that beginners have to start out ignoring things they don't
understand anyway - part of the process of learning a new language is
coming to terms with what's important and what's not in any given context.
I'm not saying I'm a big fan of the addition to the type sig of ($), and
would definitely appreciate a flag to switch it off, but I don't think
this'll make it significantly harder to teach my next victims.
Cheers,
David
On 6 February 2016 at 18:14, Manuel Gómez
The primitives that GHC uses to implement arrays, references and the like live in #. We then wrap them in something in * before exposing them to
On Sat, Feb 6, 2016 at 12:42 PM, Edward Kmett
wrote: the user, but you can shave a level of indirection by knowing what lives in # and what doesn't.
Yes! Let’s not forget, of course, that these (or similar) have been in GHC for many, many years, right in the Prelude:
```
:i Int Char Float Double IO Integer data Int = GHC.Types.I# GHC.Prim.Int# data Char = GHC.Types.C# GHC.Prim.Char# data Float = GHC.Types.F# GHC.Prim.Float# data Double = GHC.Types.D# GHC.Prim.Double# newtype IO a = GHC.Types.IO (GHC.Prim.State# GHC.Prim.RealWorld -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #)) data Integer = integer-gmp-1.0.0.0:GHC.Integer.Type.S# !GHC.Prim.Int# | integer-gmp-1.0.0.0:GHC.Integer.Type.Jp# {-# UNPACK #-}integer-gmp-1.0.0.0:GHC.Integer.Type.BigNat | integer-gmp-1.0.0.0:GHC.Integer.Type.Jn# {-# UNPACK #-}integer-gmp-1.0.0.0:GHC.Integer.Type.BigNat
Stepping outside the Prelude, yet well within beginner territory, brings even more fun:
:i Map Set data Map k a = containers-0.5.6.2:Data.Map.Base.Bin {-# UNPACK #-}containers-0.5.6.2:Data.Map.Base.Size !k a !(Map k a) !(Map k a) | containers-0.5.6.2:Data.Map.Base.Tip data Set a = containers-0.5.6.2:Data.Set.Base.Bin {-# UNPACK #-}containers-0.5.6.2:Data.Set.Base.Size !a !(Set a) !(Set a) | containers-0.5.6.2:Data.Set.Base.Tip
Unboxed types, the UNPACK pragma, references to GHC.Prim (which easily lead to confusing exploration), unboxed tuples, an unboxed State monad, RealWorld, bang patterns, unexported constructors, implementation details for abstract types… all of them available right from the prompt of the Prelude using the main tool for exploratory learning that beginners rely on. I’m not saying this is a good thing and I’m not saying this should be fixed. I’m not even saying this is comparable to the situation with $ and I’m likewise not saying presenting these concepts to beginners should be thought of as comparable to presenting levity polymorphism to beginners. It is nonetheless relevant context to this discussion; the Prelude has always had concepts unfriendly to beginners readily available, and Haskell beginner teachers have always had to work around these issues. Students have always asked about these things. > But even if you never care about #, Int, Double, etc. are of kind *, > Functors are of kind * -> *, etc. so to talk about the type of types at all > you need to be able to talk about these concepts at all with any rigor, and > to understand why Maybe Maybe isn't a thing. In my personal teaching experience, it is extremely helpful to discuss kinds in the first introduction of type constructors, after covering types with no parameters. This is especially helpful in discussing how the hierarchy leading to Monad works, and why things like «instance Functor (Tree Int) where …» don’t make sense and why «instance Functor Tree where …» must be parametric in the type of the thing in the tree, which in turn motivates a lot more discussion. Teaching kinds is teaching Haskell basics. It is not an advanced topic. It ought to be covered near the very first lessons on Haskell for absolute beginners. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

There's a lot of stuff there you don't need as a beginner. The line beginning 'Error' is a bit scary, as is the 'Warning'. The advice to run 'stack init' is not good advice.
I understand your point about 'Error', but I still think most people are more likely to dismiss everything before `Prelude>` as line noise than to ignore stuff that is printed after they type `:t ($)`, especially if they've already used `:t` to check the types of other things. Don't get me wrong, I'm generally all in for unlifted/unboxed types, data kinds, levity polymorphism, etc, it's just that I remember the time when the tipe signature of `>>=` looked scary and the `Foldable` and `Traversable` type classes seemed like black magic to me. I hesitate to suggest this, as someone who didn't write any Haskell for a couple of months now, but maybe the already mentioned suggestion of having a ($) that only works for lifted types and (#$) that is levity polymorphic would be a good choice? Best regards, Marcin Mrotek

On Sun, Feb 07, 2016 at 01:55:25AM +0100, Marcin Mrotek wrote:
I hesitate to suggest this, as someone who didn't write any Haskell for a couple of months now, but maybe the already mentioned suggestion of having a ($) that only works for lifted types and (#$) that is levity polymorphic would be a good choice?
If we're going to introduce levity polymorphism then I think separate operators are a good place to start. Unifying them can happen once everyone has had a chance to get used to the idea.

Hi, friends! I want to share my own feelings about type signatures. It is always hard for me to read type signatures with class constraints, because first I need to spot that there is =>, then I have to split type signature in my mind to constraint part and actual signature part. I think having constraints before signature when defining things is something that eases source parsing and etc., but wouldn't type signatures become a bit more readable if we put constraints after actual signature when printing it in GHCi (and maybe in Haddock), e.g.: ($) :: (a -> b) -> a -> b forall r :: RuntimeRep a :: * b :: TYPE r

On 7 February 2016 at 22:08, Geraldus
Hi, friends! I want to share my own feelings about type signatures. It is always hard for me to read type signatures with class constraints, because first I need to spot that there is =>, then I have to split type signature in my mind to constraint part and actual signature part. I think having constraints before signature when defining things is something that eases source parsing and etc., but wouldn't type signatures become a bit more readable if we put constraints after actual signature when printing it in GHCi (and maybe in Haddock), e.g.:
($) :: (a -> b) -> a -> b forall r :: RuntimeRep a :: * b :: TYPE r
If this is only how ghci types/prints it, then it makes it much more difficult (if not impossible) to just copy/paste the resulting type into your code. This also makes it much more verbose: it might be useful for longer type signatures (especially with large constraints) but not for the majority of them. I also like being able to see the constraints first so that I know what they are before reading the actual type. -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com http://IvanMiljenovic.wordpress.com

On Sat, Feb 06, 2016 at 12:12:47PM -0500, Edward Kmett wrote:
The primitives that GHC uses to implement arrays, references and the like live in #. We then wrap them in something in * before exposing them to the user, but you can shave a level of indirection by knowing what lives in # and what doesn't.
But even if you never care about #, Int, Double, etc. are of kind *, Functors are of kind * -> *, etc. so to talk about the type of types at all you need to be able to talk about these concepts at all with any rigor, and to understand why Maybe Maybe isn't a thing.
(This question is for my own edification and is not meant to be a point in the current debate) If we were inventing a language from the beginning, would it be strictly necessary to have two kinds? Could we have just an unboxed kind #, and have a box be an explicit type constructor? If the type constructor were called 'P' (standing for pointer) then we could have id :: P a -> P a data [a] = (P a) : [a] | [] etc. Does this thing seem remotely plausible to people who know clever type theory? Tom

On Sun, Feb 7, 2016 at 10:39 AM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
If we were inventing a language from the beginning, would it be strictly necessary to have two kinds?
Could we have just an unboxed kind #, and have a box be an explicit type constructor?
Does this thing seem remotely plausible to people who know clever type theory?
Idris tries to do this with their Lazy type -- to somewhat mixed success, so yes, it is a thing that can be done in a language designed from scratch.

Tom Ellis
On Fri, Feb 05, 2016 at 07:19:25PM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?
Does anyone have any idea about this? What is it about ($) that means it needs a new funky type whereas (apparently) nothing else does?
The first (albeit rather unconvincing) example I can think of is be something like, getI# :: Int -> Int# getI# (I# n#) = n# n# :: Int# n# = getI# $ 5 + 8 Richard likely has something a bit less contrived though. This does raise the question of why ($) is generalized, yet (.) is not, (.) :: forall (l :: Levity) a b (c :: TYPE l). (b -> c) -> (a -> b) -> (a -> c) (.) f g x = f (g x) Cheers, - Ben

On Sat, Feb 06, 2016 at 01:27:00PM +0100, Ben Gamari wrote:
Tom Ellis
writes: On Fri, Feb 05, 2016 at 07:19:25PM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?
Does anyone have any idea about this? What is it about ($) that means it needs a new funky type whereas (apparently) nothing else does?
The first (albeit rather unconvincing) example I can think of is be something like,
getI# :: Int -> Int# getI# (I# n#) = n#
n# :: Int# n# = getI# $ 5 + 8
Richard likely has something a bit less contrived though.
I hope there's something less contrived, because if the benefit is "you get to use $ to apply functions whose return type is not of kind *" then the power to weight ratio of this is extremely low. Is it also something to do with the special treatment that $ gets in the compiler, to allow 'runST $ do'? https://www.mail-archive.com/glasgow-haskell-users@haskell.org/msg18923.html
This does raise the question of why ($) is generalized, yet (.) is not,
(.) :: forall (l :: Levity) a b (c :: TYPE l). (b -> c) -> (a -> b) -> (a -> c) (.) f g x = f (g x)
Quite.

Tom Ellis
On Sat, Feb 06, 2016 at 01:27:00PM +0100, Ben Gamari wrote:
The first (albeit rather unconvincing) example I can think of is be something like,
getI# :: Int -> Int# getI# (I# n#) = n#
n# :: Int# n# = getI# $ 5 + 8
Richard likely has something a bit less contrived though.
I hope there's something less contrived, because if the benefit is "you get to use $ to apply functions whose return type is not of kind *" then the power to weight ratio of this is extremely low.
Is it also something to do with the special treatment that $ gets in the compiler, to allow 'runST $ do'?
https://www.mail-archive.com/glasgow-haskell-users@haskell.org/msg18923.html
To this the best of my knowledge, no. This would require impredicative polymorphism which Richard's work does not provide. There is (was?), however, active work on this front as well [1]. Cheers, - Ben [1] https://ghc.haskell.org/trac/ghc/wiki/ImpredicativePolymorphism/Impredicativ...

On Sat, Feb 06, 2016 at 12:31:57PM +0000, Tom Ellis wrote:
On Sat, Feb 06, 2016 at 01:27:00PM +0100, Ben Gamari wrote:
Tom Ellis
writes: On Fri, Feb 05, 2016 at 07:19:25PM +0000, Tom Ellis wrote:
On Fri, Feb 05, 2016 at 01:13:23PM -0500, Richard Eisenberg wrote:
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently.
Is there a list of situations where ($) is used currently that give rise to this need?
Does anyone have any idea about this? What is it about ($) that means it needs a new funky type whereas (apparently) nothing else does?
The first (albeit rather unconvincing) example I can think of is be something like,
getI# :: Int -> Int# getI# (I# n#) = n#
n# :: Int# n# = getI# $ 5 + 8
Richard likely has something a bit less contrived though.
I hope there's something less contrived, because if the benefit is "you get to use $ to apply functions whose return type is not of kind *" then the power to weight ratio of this is extremely low.
No one has suggested anything less contrived, so I'm going to assume this encompasses all use cases for the new type. in which case may I make a counter proposal: Give Prelude.($) a truthful type of '(a -> b) -> a -> b' and put the generalized version in a separate module, at least for now? I note that with the Foldable/Traversable Prelude we had * Foldable and Traversable in separate modules for a long, long time * A long discussion about merging them * Lots and lots of people using Foldable and Traversable None of these cases seems to apply to generalized ($) so I don't think it warrants inclusion in base (yet). (This is not to suggest that the work on the type system to make this sort of polymorphism is not technically excellent -- I just think we need to hold fire regarding merging such changes to base). Tom

On Feb 7, 2016, at 10:11 AM, Tom Ellis
No one has suggested anything less contrived, so I'm going to assume this encompasses all use cases for the new type. in which case may I make a counter proposal:
Give Prelude.($) a truthful type of '(a -> b) -> a -> b' and put the generalized version in a separate module, at least for now?
I would agree with this... except that the version of ($) in base in 7.8 and 7.10 already *was* generalized in this way. But no one got as itchy about the OpenKind that appears in 7.10's `:i $` as they are about the guck that appears in 8.0's `:t ($)`. So moving it out now would break code in the wild like https://ghc.haskell.org/trac/ghc/ticket/8739 Just to amplify this point: the generalization of ($) that we are debating **is not new**. The way it's rendered in GHCi is new, however. Richard

On Sun, 7 Feb 2016 at 17:16 Richard Eisenberg
I would agree with this... except that the version of ($) in base in 7.8 and 7.10 already *was* generalized in this way. But no one got as itchy about the OpenKind that appears in 7.10's `:i $` as they are about the guck that appears in 8.0's `:t ($)`. So moving it out now would break code in the wild like https://ghc.haskell.org/trac/ghc/ticket/8739
My GHC 7.10 doesn't seem to have any OpenKind stuff: GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help Prelude> :i $ ($) :: (a -> b) -> a -> b -- Defined in ‘GHC.Base’ infixr 0 $

On Sun, Feb 07, 2016 at 10:15:49AM -0500, Richard Eisenberg wrote:
On Feb 7, 2016, at 10:11 AM, Tom Ellis
wrote: No one has suggested anything less contrived, so I'm going to assume this encompasses all use cases for the new type. in which case may I make a counter proposal:
Give Prelude.($) a truthful type of '(a -> b) -> a -> b' and put the generalized version in a separate module, at least for now?
I would agree with this... except that the version of ($) in base in 7.8 and 7.10 already *was* generalized in this way. But no one got as itchy about the OpenKind that appears in 7.10's `:i $` as they are about the guck that appears in 8.0's `:t ($)`. So moving it out now would break code in the wild like https://ghc.haskell.org/trac/ghc/ticket/8739
This sounds a bit like throwing good money after bad. The levity polymorphic behaviour of ($) in 7.8 and 7.10 was not advertised in its type, nor in the documentation: https://hackage.haskell.org/package/base-4.8.2.0/docs/src/GHC.Base.html#%24 If people were relying on it to do something non-Haskell 2010, undocumented, GHC specific, then that's sad for them. But there are probably one hundred times as many people (literally) who would be startled to see the "correct" type pop up in GHC 8. Why not move the polymorphic version to a library and all those who really need it can get it from there. In time, when it has demonstrated itself to be an indisposable generalization, then it can be moved to base.
Just to amplify this point: the generalization of ($) that we are debating **is not new**. The way it's rendered in GHCi is new, however.
Sure I get that. It's just that few really knew that's how it was, because it wasn't advertised as being as it was, so few could question it :) (I've known for a while ($) has some magic around runST, but two days ago was the first time I heard about it being levity polymorphic.) Tom

Richard,
I appreciate your response and have some genuine questions about how you
see the Language growing in the future. As much as I am a principled
developer in terms of adhering closely to the truth as possible, I also
view code as a product that needs to "customers" to be successful. In order
for that to happen, it needs to easily accessible and easy to understand.
I learned Haskell almost entirely by looking at existing projects and
exploring the very awesome Hackage documentation. What would be the hackage
definition for ($)? Would it be `($) :: forall (r :: RuntimeRep) (a :: *)
(b :: TYPE r). (a -> b) -> a -> b` with an asterisk that says "*For
beginners: ($) :: (a -> b) -> a -> b"
Would there be a "Simple Hackage"?
It would be interesting for me to see how the skill levels of Haskell are
distributed. In most languages it would look like a pyramid with a small
group advanced developers on top and a mountain of people underneath.
Haskell seems to be pushing towards the inverse, in which to code and
understand standard, non beginners mode haskell you have to be "advanced".
The barrier to entry looks to be increasing.
I agree with Christopher Allen and also do not agree with your assessment
and comparison to the unnecessary syntax in Java. You can explain that
program using simple english. That is why it was used for so many years as
an introductory language.
How do you explain `forall (r :: RuntimeRep) (a :: *) (b :: TYPE r).` using
simple english?
I think its important to identify who you want your "customers" to be. If
you only want the most advanced type theorists to use the language, that is
perfectly fine, but what you lose are thousands of developers that can
benefit the Haskell community without having to know advanced Typing.
Needing a "Beginners" mode in a language is *not* a feature, its a
fundamental design flaw. It shows that the language was not sufficiently
thought out and designed for everyone.
Its extremely important to not lose touch with the people that make the
community; the newcomers. Sacrificing the 99% of beginner and intermediate
haskellers for the 1%, I believe is a step in the wrong direction.
--
Kyle
On Fri, Feb 5, 2016 at 10:13 AM, Richard Eisenberg
Perhaps it will aid the discussion to see that the type of ($) will, for better or worse, be changing again before 8.0.
The problem is described in GHC ticket #11471. The details of "why" aren't all that important for this discussion, but the resolution might be. The new (hopefully final!) type of ($) will be:
($) :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). (a -> b) -> a -> b
Once again, it's easy enough to tweak the pretty-printer to hide the complexity. But perhaps it's not necessary. The difference as far as this conversation is concerned is that Levity has been renamed to RuntimeRep. I think this is an improvement, because now it's not terribly hard to explain:
--- 1. Types of kind * have values represented by pointers. This is the vast majority of data in Haskell, because almost everything in Haskell is boxed. 2. But sometimes, we don't care how a value is represented. In this case, we can be polymorphic in the choice of representation, just like `length` is polymorphic in the choice of list element type. 3. ($) works with functions whose result can have any representation, as succinctly stated in the type. Note that the argument to the function must be boxed, however, because the implementation of ($) must store and pass the argument. It doesn't care at all about the result, though, allowing for representation-polymorphism.
In aid of this explanation, we can relate this all to Java. The reference types in Java (e.g., Object, int[], Boolean) are all like types of kind *. The primitive types in Java (int, boolean, char) do not have kind *. Java allows type abstraction (that is, generics) only over the types of kind *. Haskell is more general, allowing abstraction over primitive types via representation polymorphism. ---
Could this all be explained to a novice programmer? That would be a struggle. But it could indeed be explained to an intermediate programmer in another language just learning Haskell.
For point of comparison, Java is widely used as a teaching language. And yet one of the simplest programs is
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } }
When I taught Java (I taught high-school full time for 8 years), I would start with something similar to this and have to tell everyone to ignore 90% of what was written. My course never even got to arrays and `static`! That was painful, but everyone survived. This is just to point out that Haskell isn't the only language with this problem. Not to say we shouldn't try to improve!
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently. The old type for ($) was just a plain old lie. Now, at least, we're not lying. So, do we 1) lie, 2) allow the language to grow, or 3) avoid certain growth because it affects how easy the language is to learn? I don't really think anyone is advocating for (3) exactly, but it's hard to have (2) and not make things more complicated -- unless we have a beginners' mode or other features in, say, GHCi that aid learning. As I've said, I'm in full favor of adding these features.
Richard
On Feb 5, 2016, at 12:55 PM, Kyle Hanson
wrote: I am also happy the discussion was posted here. Although I don't teach Haskell professionally, one of the things I loved to do was show people how simple Haskell really was by inspecting types and slowly putting the puzzle pieces together.
Summary of the problem for others:
From *Takenobu Tani*
Before ghc7.8:
Prelude> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($) ($) :: (a -> b) -> a -> b
Beginners should only understand about following:
* type variable (polymorphism)
After ghc8.0:
Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t ($) ($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
With this change it looks like I will no longer be able to keep `$` in my toolbox since telling a beginner its "magic" goes against what I believe Haskell is good at, being well defined and easy to understand (Not well defined in terms of Types but well defined in terms of ability to precisely and concisely explain and define whats going on).
It looks like where the discussion is going is to have these types show by default but eventually have an Alternative prelude for beginners.
From *Richard Eisenberg:*
- It's interesting that the solution to the two problems Takenobu pulls out below (but others have hinted at in this thread) is by having an alternate Prelude for beginners. I believe that having an alternate beginners' Prelude is becoming essential. I know I'm not the first one to suggest this, but a great many issues that teachers of Haskell have raised with me and posts on this and other lists would be solved by an alternate Prelude for beginners.
I don't like the idea of fragmenting Haskell into "beginners" and "advanced" versions. Its hard enough to get people to believe Haskell is easy. If they see that they aren't using the "real" prelude, Haskell will still be this magic black box that is too abstract and difficult to understand. If they have to use a "dumbed down" version of Haskell to learn, its not as compelling.
There is something powerful about using the same idiomatic tools as the "big boys" and have the tools still be able to be easy to understand.... by default. Adding complexity to the default Haskell runs the risk of further alienating newcomers to the language who have a misconception that its too hard.
Admittedly, I am not well informed of the state of GHC 8.0 development and haven't had time to fully look into the situation. I am very interested to see where this conversation and the default complexity of Haskell goes.
-- Kyle
On Fri, Feb 5, 2016 at 8:26 AM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Fri, Feb 05, 2016 at 05:25:15PM +0100, Johannes Waldmann wrote:
What's changed?
I was referring to a discussion on ghc-devs, see https://mail.haskell.org/pipermail/ghc-devs/2016-February/011268.html and mixed up addresses when replying.
I'm glad you did, because this is the first I've heard of it! _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

How do you explain `forall (r :: RuntimeRep) (a :: *) (b :: TYPE r).` using simple english?
"for all 'a's that are lifted types and 'b's that are types of any runtime representation 'r'..." I don't really want to argue what is "simple english". I'd agree that Haskell's syntax is becoming more and more inadequate for expressing ideas that are being introduced to the language, though. Best regards, Marcin Mrotek

On Fri, Feb 5, 2016 at 2:46 PM, Kyle Hanson
I think its important to identify who you want your "customers" to be. If you only want the most advanced type theorists to use the language, that is perfectly fine, but what you lose are thousands of developers that can benefit the Haskell community without having to know advanced Typing.
Needing a "Beginners" mode in a language is *not* a feature, its a fundamental design flaw. It shows that the language was not sufficiently thought out and designed for everyone.
Its extremely important to not lose touch with the people that make the community; the newcomers. Sacrificing the 99% of beginner and intermediate haskellers for the 1%, I believe is a step in the wrong direction.
I'm sympathetic, but the same arguments were made against the Foldable-Traversable Proposal. See for instance http://neilmitchell.blogspot.com/2014/10/why-traversablefoldable-should-not-... Since that wound up going in, I think this ship has sailed. Types are going to become increasingly polymorphic in the Prelude. Though I wish this weren't so I've come to accept it, and I doubt attacking it head on is going to get anywhere.

We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently. The old type for ($) was just a plain old lie. Now, at least, we're not lying. So, do we 1) lie, 2) allow the language to grow, or 3) avoid certain growth because it affects how easy the language is to learn? I don't really think anyone is advocating for (3) exactly, but it's hard to have (2) and not make things more complicated -- unless we have a beginners' mode or other features in, say, GHCi that aid learning. As I've said, I'm in full favor of adding these features.
The old type for ($) is only a lie when the MagicHash extension is turned on. Otherwise, it is not a lie. I think the best solution is to pretty print the type depending on what language pragmas are in use. In GHCI, this would be trivial. The much harder case is haddock documentation. I think a good way around this would be an eventual patch to haddock that allows the user to select which extensions they want to use when browsing documentation. There's a lot of usability issues that would need to be resolved with this still, but it reduces this technical discussion we're having down to a design discussion. It also nicely lets the user specify the level of difficulty they want their prelude to be without causing incompatibilty with users who want a different level of prelude.

off-topic but still may be relevant. my apologies if it isn't: isn't a good programming language - simple and unambiguous? isn't programming language as much about communicating with other programmers as about instructing compiler? look at the game of chess: limited number of clear rules allow for many complex and rich games. clarity, convenience + library coverage would draw users and keep them, I think. Good shiny tools attract interest. However it's what you could make with these tools matters.

On Fri, Feb 5, 2016 at 6:21 PM, Mike Izbicki
We're in a bit of a bind in all this. We really need the fancy type for ($) so that it can be used in all situations where it is used currently. The old type for ($) was just a plain old lie. Now, at least, we're not lying. So, do we 1) lie, 2) allow the language to grow, or 3) avoid certain growth because it affects how easy the language is to learn? I don't really think anyone is advocating for (3) exactly, but it's hard to have (2) and not make things more complicated -- unless we have a beginners' mode or other features in, say, GHCi that aid learning. As I've said, I'm in full favor of adding these features.
The old type for ($) is only a lie when the MagicHash extension is turned on. Otherwise, it is not a lie. I think the best solution is to pretty print the type depending on what language pragmas are in use. In GHCI, this would be trivial. The much harder case is haddock documentation.
Note: The old type of ($) has always been a lie, even without MagicHash, a much stronger lie because the true type of ($) can't even be written in the language today. You can instantiate both the source and target types of ($) to polytypes, not just monotypes. This lets us use ($) in situations like runST $ do ... Having it infer a RankNType through its magical type inference rule there doesn't require an extension on the behalf of the user, even if runST required them at the definition site. -Edward
I think a good way around this would be an eventual patch to haddock that allows the user to select which extensions they want to use when browsing documentation. There's a lot of usability issues that would need to be resolved with this still, but it reduces this technical discussion we're having down to a design discussion. It also nicely lets the user specify the level of difficulty they want their prelude to be without causing incompatibilty with users who want a different level of prelude. _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

Kyle Hanson
writes:
Prelude> :t ($)
($) :: (a -> b) -> a -> b
Prelude> :t ($)
($) :: forall (w :: GHC.Types.Levity) a (b :: TYPE w). (a -> b) -> a -> b
I wonder if it could elide that information when -fprint-explicit-foralls is not enabled? The (a :: *) is not enough to warrant a forall in the former case, so 'b :: TYPE w' where 'w :: GHC.Types.Levity' perhaps shouldn't be enough to warrant it in the latter. -- John Wiegley GPG fingerprint = 4710 CF98 AF9B 327B B80F http://newartisans.com 60E1 46C4 BD1A 7AC1 4BA2

As Manuel wrote:
I expect that every single person teaching Haskell is going to be unhappy about it.
If I were in the happy position of still teaching Haskell, I would be unhappy about it. In my own use of Haskell, I've only played briefly with # types and then decided to leave them to library writers. I'd certainly never heard of "levity polymorphism" before. I can agree that for a functional language, being able to apply any reasonable function to any reasonable matching argument has to be doable, but such a fundamental operation surely needs to be simple to describe? Is there a complete lit of all the affected functions? Is a complete list even possible?

I can agree that for a functional language, being able to apply any reasonable function to any reasonable matching argument has to be doable, but such a fundamental operation surely needs to be simple to describe?
But it is already rather simple conceptually, it's just that Haskell's kind signature syntax makes it look hairy. I mean, if you squint enough, this: ($) :: forall (r :: RuntimeRep) (a :: *) (b :: TYPE r). (a -> b) -> a -> b could be written like: ($) :: RuntimeRep r => (a :: TYPE Lifted) (b :: TYPE r). (a -> b) -> a -> b or like I've previously suggested (if * is the default kind and wildcards are allowed in kinds): ($) :: forall a (b :: TYPE _). (a -> b) -> a -> b I mean, perhaps the syntax could be improved, but the information that: * a is lifted and boxed * b can have any kind that has a runtime representation (so unlifted and unboxed types are ok, but data kinds are not) has to go somewhere if the ability to write levity-polymorphic functions is to be given to the user, rather than only available as a one-off hack in the compiler. Perhaps going fully dependently typed and giving up on the distinction between values and types would change that; I'm not sure how for example Idris would handle levity polymorphism. Best regards, Marcin Mrotek
participants (29)
-
Bardur Arantsson
-
Ben Gamari
-
butrfeld
-
Carter Schonwald
-
Christopher Allen
-
Colin Adams
-
David Turner
-
Edward Kmett
-
Geraldus
-
Imants Cekusins
-
Ivan Lazar Miljenovic
-
Joachim Durchholz
-
Johannes Waldmann
-
John Wiegley
-
Kyle Hanson
-
Manuel Gómez
-
Marcin Mrotek
-
Miguel Mitrofanov
-
Mike Izbicki
-
Omari Norman
-
Raphael Gaschignard
-
Richard A. O'Keefe
-
Richard Eisenberg
-
Rustom Mody
-
Strikingwolf2012 .
-
Takenobu Tani
-
Tom Ellis
-
Tristan Seligmann
-
Will Yager