beginners [1].
I'll also try to draw simple illustrations about new ($).
Thank you for your great efforts.
Post by Richard EisenbergIt 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 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 (scratch.mit.edu),
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
+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!
Post by Christopher AllenChanging 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
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.
Post by Richard EisenbergTypes 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.
Post by Richard EisenbergPerhaps 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.
($) :: 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
---
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
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.
From *Takenobu Tani*
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude> :t ($)
($) :: (a -> b) -> a -> b
* type variable (polymorphism)
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 <
Post by Johannes WaldmannI 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
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________
ghc-devs mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
--
Chris Allen
Currently working on http://haskellbook.com
_______________________________________________
ghc-devs mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
_______________________________________________
ghc-devs mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs