Language complexity & beginners (Was: New type of ($) operator in GHC 8.0 is problematic)

Takenobu Tani takenobu.hs at
Sat Feb 6 00:28:58 UTC 2016


I tried to draw informal illustrations about Foldable signatures for
beginners [1].
I'll also try to draw simple illustrations about new ($).

Of course I like Haskell's beautiful abstraction :)
Thank you for your great efforts.



2016-02-06 9:09 GMT+09:00 Richard Eisenberg <eir at>:

> 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.
> I fully agree with George's suggestion below that the default Prelude
> should be the beginner's Prelude. I believe I have argued this stance in
> the past, but louder voices prevailed. Perhaps I was wrong in branding: we
> should have a proper Prelude as the default, and make available a super
> whiz-bang advanced Prelude as well. I'm never very good about branding. I'd
> lend strong support to someone who articulates a concrete move in this
> direction, but I don't have the bandwidth to spearhead it myself.
> Despite the various arguments saying that the bits in Java are easier to
> understand than the bits in ($), I'm quite unconvinced. (Particularly about
> `static`. Even `class` is hard for true beginners.) And the boxed/unboxed
> distinction does come up early in Java: just try to write an ArrayList<int>
> and now you need to know about boxed types and unboxed ones.
> Chris's point that "it's not about the name" is valid. The Levity -->
> RuntimeRep change is not about the name, but about the functionality.
> Levity distinguished only between lifted and unlifted; RuntimeRep
> distinguishes between boxed/lifted, boxed/unlifted, and all the unboxed
> types with their different widths. I'm just clarifying that it's not simply
> a cosmetic name-change.
> The old type of ($) was always a lie. -XMagicHash just changes the parser,
> allowing the # suffix. It is only by convention that most (all?) unlifted
> things end in #. The old type of ($) was perhaps a harmless lie, but a lie
> nonetheless.
> Are we comfortable with lying? (Believe me, I'm not trying to impose some
> moral dimension to simplifying output!) In my mind, lying about types like
> this is in the same space as having a beginner's Prelude. And people will
> constantly discover that we're lying and get very confused. Having a whole
> host of flags that tell GHC to lie less is somewhat like having two
> versions of the language... only the differences manifest only in output
> instead of input.
> If we are comfortable with lying in this way: as I've offered, I can hide
> the type of ($) (and other representation-polymorphic things) behind a
> flag. Easy to do.
> Another great question that has come up is about Haddock output (Hackage).
> I think Haddock needs to add a facility where library authors can include
> specializations of an overly general type. This can be done in commentary,
> but it's not as prominent. Such a new feature would address the ($)
> problem, as ($) :: forall (a :: *) (b :: *). (a -> b) -> a -> b is a
> specialization of its real type. It would also help a great deal with
> FTP-related generalizations.
> I also want to respond directly to Kyle's comments:
> 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.
> Rest assured, I want my "customers" to be everyone who wants to program.
> I've volunteered to teach a bit of Haskell to high schoolers, and I'd love
> a shot at a course where I teach it to people who have never programmed.
> 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.
> On an intuitive level, this rings true for me. But when I think about the
> details, I'm less convinced. For example, take Scratch (,
> which is wonderfully easy to learn and gives kids (and adults!) a great
> deal of fun. Yet it's painful to use when you know more. And the Racket
> folks have invested a lot of time in coming up with a curriculum to go with
> their language, and they explicitly have expertise levels. Needing these
> levels may just be part of the game.
> So, rest assured, I remain very receptive to these concerns. And I'd love
> concrete help in putting them to rest.
> Richard
> On Feb 5, 2016, at 6:30 PM, George Colpitts <george.colpitts at>
> wrote:
> +1 for Christopher's email
> Richard, I disagree with  "But it could indeed be explained to an
> intermediate programmer in another language just learning Haskell." Your
> explanation is good but it assumes you have already explained "types of
> kind *" and the boxed vs unboxed distinction. Admittedly the latter should
> be understood by most Java programmers but I doubt that intermediate
> programmers in other languages do. If I did have to explain "$" I would
> say, for now think of it in terms of it's pre 8.0 type. Alternatively avoid
> mentioning "$" to beginners. I don't believe it is in Hutton's book or any
> of Bird's although I might be wrong.
> Most intermediate programmers in another language struggle a lot with
> learning monads, witness all the monad tutorials. Absorbing monads is
> central, there is a lot that has to be explained before that. Minimizing
> that material would be a good thing.
> I have mixed feelings about a beginner's prelude best summarized by saying
> the proposed beginner's prelude should be the standard prelude and the
> current one should be an advanced prelude. If we have a beginner's prelude
> I feel we are saying that this is a hard to understand research language
> and we hope that someday you have enough education, energy and tenacity to
> get to the point where you understand it. If we do it the other way we are
> saying you have what you need but if you want more there is lots!
> On Fri, Feb 5, 2016 at 3:05 PM, Christopher Allen <cma at>
> 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.
>> 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 <eir at>
>> wrote:
>>> 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 <me at> 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 at> 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
>>>> >
>>>> > 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 at
>>> _______________________________________________
>>> Haskell-Cafe mailing list
>>> Haskell-Cafe at
>>> _______________________________________________
>>> ghc-devs mailing list
>>> ghc-devs at
>> --
>> Chris Allen
>> Currently working on
>> _______________________________________________
>> ghc-devs mailing list
>> ghc-devs at
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the ghc-devs mailing list