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

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 (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
On Feb 5, 2016, at 6:30 PM, George Colpitts
+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
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
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
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
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
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

On Fri, Feb 5, 2016 at 7:09 PM, Richard Eisenberg
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.
This goes hand in hand with Artyom's suggestion of a warning in GHCi after showing the simpler type. I'm thinking of a flag which enables/disables printing the simplest type with warning (in GHCi) or footnote (or otherwise, in Haddock). We can have the default behavior of the flag be either printing the simpler type + extra (warning/footnote) or printing the longer type and include a reference in our learning materials that beginners and people confused by the long, complex and real type, can use --use-simpler-types flag. -- Mihai Maruseac (MM) "If you can't solve a problem, then there's an easier problem you can solve: find it." -- George Polya

I’ve amended my suggestion to say basically “this type is a slight lie, here’s a flag/command to see the true type” – this way we aren’t scaring people with implementation guts, merely letting them see the guts for themselves and then think “I don’t care about this” (which is, I think, exactly what should happen; the worst scenario here is that the beginner falls into the “I’m an advanced user, I need all features, I need to know everything, so I’ll enable the flag” trap – which is why it’s important not to call it “an advanced type” or mention “if you know what you’re doing” or anything else like that). I don’t agree that levity can be compared to Java’s “class” or “static” – not because it’s harder to understand, but because it’s much less widely used; I don’t feel that you need to know about levity in order to be a good Haskeller. Also, unboxed types don’t imply knowledge of levity – for instance, I’ve been successfully using unboxed types for a while, but I only found out about the true type of |($)| by complete accident (I think I queried the kind of |->| and then got curious about question marks). Of On 02/06/2016 03:27 AM, Mihai Maruseac wrote:
On Fri, Feb 5, 2016 at 7:09 PM, Richard Eisenberg
wrote: 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. This goes hand in hand with Artyom's suggestion of a warning in GHCi after showing the simpler type.
I'm thinking of a flag which enables/disables printing the simplest type with warning (in GHCi) or footnote (or otherwise, in Haddock). We can have the default behavior of the flag be either printing the simpler type + extra (warning/footnote) or printing the longer type and include a reference in our learning materials that beginners and people confused by the long, complex and real type, can use --use-simpler-types flag.

A bit of time away from my keyboard has revealed a natural way to solve this problem and others: be more like Idris.
Normally, of course, I'm thinking about how Haskell's type system can be more like Idris's. But that's not what I mean here. I want Haskell's interface to be more like Idris's. Imagine this interchange:
λ> :t ($)
($) :: (a -> b) -> a -> b
-- click on the type
($) :: forall a b. (a -> b) -> a -> b
-- click on the a
($) :: forall (a :: *) b. (a -> b) -> a -> b
-- click on the b
($) :: forall (a :: *) (b :: *). (a -> b) -> a -> b -- where b's kind has a different color than usual
-- click on b's kind
($) :: forall {r :: RuntimeRep} (a :: *) (b :: TYPE r). (a -> b) -> a -> b
-- mouseover RuntimeRep or TYPE reveals a tooltip
"($) is representation-polymorphic, meaning that `b` can have an arbitrary runtime representation. Please see http://.... for more details."
Similarly, classes would render in a special color, allowing you to click on them and choose to instantiate the type at a few in-scope instances of the class at hand, changing Foldable f => f a -> Int to the much simpler [a] -> Int.
This is not a minor engineering project, but it would reap wonderful rewards, addressing the problems in this thread and more. No more lying (because all lies are clickable), no more fragmented language, no more brakes on development.
Evidently, Chris already agrees with this proposal: #10073 (https://ghc.haskell.org/trac/ghc/ticket/10073)
Also see #8809 (https://ghc.haskell.org/trac/ghc/ticket/8809)
Any volunteers to implement this? :)
Richard
On Feb 5, 2016, at 7:47 PM, Artyom
I’ve amended my suggestion to say basically “this type is a slight lie, here’s a flag/command to see the true type” – this way we aren’t scaring people with implementation guts, merely letting them see the guts for themselves and then think “I don’t care about this” (which is, I think, exactly what should happen; the worst scenario here is that the beginner falls into the “I’m an advanced user, I need all features, I need to know everything, so I’ll enable the flag” trap – which is why it’s important not to call it “an advanced type” or mention “if you know what you’re doing” or anything else like that).
I don’t agree that levity can be compared to Java’s “class” or “static” – not because it’s harder to understand, but because it’s much less widely used; I don’t feel that you need to know about levity in order to be a good Haskeller. Also, unboxed types don’t imply knowledge of levity – for instance, I’ve been successfully using unboxed types for a while, but I only found out about the true type of ($) by complete accident (I think I queried the kind of -> and then got curious about question marks). Of
On 02/06/2016 03:27 AM, Mihai Maruseac wrote:
On Fri, Feb 5, 2016 at 7:09 PM, Richard Eisenberg
wrote: 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. This goes hand in hand with Artyom's suggestion of a warning in GHCi after showing the simpler type.
I'm thinking of a flag which enables/disables printing the simplest type with warning (in GHCi) or footnote (or otherwise, in Haddock). We can have the default behavior of the flag be either printing the simpler type + extra (warning/footnote) or printing the longer type and include a reference in our learning materials that beginners and people confused by the long, complex and real type, can use --use-simpler-types flag.

On Fri, Feb 5, 2016 at 9:24 PM, M Farkas-Dyck
-- click on the type
The question so remains: what would we write to a purely textual terminal?
We could write the simplest type with a "type :expand / :ex (or similar) to expand signature", eventually underlining the part that will be expanded first. -- Mihai Maruseac (MM) "If you can't solve a problem, then there's an easier problem you can solve: find it." -- George Polya

I have a bad feeling this would complicate the parser and pretty printer a lot, and thus isn't feasible, but what about allowing wildcards in kind signatures? I mean, in forall (w :: Levity) a (b :: TYPE w). (a -> b) -> a -> b the `w` variable seems superfluous, as it's only there to give it a kind signature. I think there's enough information in `TYPE w` to infer the kind (`TYPE` can only be parametrized with `Levity`?) so maybe something like this would work: forall a (b :: TYPE _). (a -> b) -> a -> b This could be "some" compromise between "lying" about the type and being scary to beginners, as at the very least all the type variables introduced are actually used in the type signature. Best regards, Marcin Mrotek

Richard,
That is by far the best idea I've read in this entire thread!!!
There should be no more lies, no "beginner-only" preludes, etc. All
information should be available on request, effortlessly, as in your
example interaction with GHCi. I don't like having to set special
flags to see/hide certain info, as it was proposed. Having to use the
flags can easily mislead people who are not aware of them and also it
is too much work.
There was an issue raised with Haddocks. It's 2016 and we can easily
make the haddocks more interactive by embedding some JavaScript to
exactly recreate your interaction with GHCi or even, as a poor mans
substitute, simply show more details on mouse hoover or have similar
design like for showing instances, etc.
Every programmer should understand the difference between boxed and
unboxed values. Period. The fact that Haskell allows for levity
polimorphism is something we should be proud of and leverage in
teaching, not hide it or lie about it.
Finally, I wanted to highlight explicit type application as a great
didactic tool. We can now nicely provide types the same way as values
to the function and I find it a great way to explain type parameters.
Best,
Michał
On Fri, Feb 5, 2016 at 8:51 PM, Richard Eisenberg
A bit of time away from my keyboard has revealed a natural way to solve this problem and others: be more like Idris.
Normally, of course, I'm thinking about how Haskell's type system can be more like Idris's. But that's not what I mean here. I want Haskell's interface to be more like Idris's. Imagine this interchange:
λ> :t ($) ($) :: (a -> b) -> a -> b -- click on the type ($) :: forall a b. (a -> b) -> a -> b -- click on the a ($) :: forall (a :: *) b. (a -> b) -> a -> b -- click on the b ($) :: forall (a :: *) (b :: *). (a -> b) -> a -> b -- where b's kind has a different color than usual -- click on b's kind ($) :: forall {r :: RuntimeRep} (a :: *) (b :: TYPE r). (a -> b) -> a -> b -- mouseover RuntimeRep or TYPE reveals a tooltip "($) is representation-polymorphic, meaning that `b` can have an arbitrary runtime representation. Please see http://.... for more details."
Similarly, classes would render in a special color, allowing you to click on them and choose to instantiate the type at a few in-scope instances of the class at hand, changing Foldable f => f a -> Int to the much simpler [a] -> Int.
This is not a minor engineering project, but it would reap wonderful rewards, addressing the problems in this thread and more. No more lying (because all lies are clickable), no more fragmented language, no more brakes on development.
Evidently, Chris already agrees with this proposal: #10073 (https://ghc.haskell.org/trac/ghc/ticket/10073)
Also see #8809 (https://ghc.haskell.org/trac/ghc/ticket/8809)
Any volunteers to implement this? :)
Richard
On Feb 5, 2016, at 7:47 PM, Artyom
wrote: I’ve amended my suggestion to say basically “this type is a slight lie, here’s a flag/command to see the true type” – this way we aren’t scaring people with implementation guts, merely letting them see the guts for themselves and then think “I don’t care about this” (which is, I think, exactly what should happen; the worst scenario here is that the beginner falls into the “I’m an advanced user, I need all features, I need to know everything, so I’ll enable the flag” trap – which is why it’s important not to call it “an advanced type” or mention “if you know what you’re doing” or anything else like that).
I don’t agree that levity can be compared to Java’s “class” or “static” – not because it’s harder to understand, but because it’s much less widely used; I don’t feel that you need to know about levity in order to be a good Haskeller. Also, unboxed types don’t imply knowledge of levity – for instance, I’ve been successfully using unboxed types for a while, but I only found out about the true type of ($) by complete accident (I think I queried the kind of -> and then got curious about question marks). Of
On 02/06/2016 03:27 AM, Mihai Maruseac wrote:
On Fri, Feb 5, 2016 at 7:09 PM, Richard Eisenberg
wrote: 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.
This goes hand in hand with Artyom's suggestion of a warning in GHCi after showing the simpler type.
I'm thinking of a flag which enables/disables printing the simplest type with warning (in GHCi) or footnote (or otherwise, in Haddock). We can have the default behavior of the flag be either printing the simpler type + extra (warning/footnote) or printing the longer type and include a reference in our learning materials that beginners and people confused by the long, complex and real type, can use --use-simpler-types flag.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

in addition to Takenobu's links, Real World Haskell explains unboxing and lifting on p.583 just to clarify, for practical use, is it safe to say that "boxed" and "lifted" are synonyms? you see, term "boxed" is used in other languages. I assumed "lifting" related to monads. Hence the confusion.

is it safe to say that "boxed" and "lifted" are synonyms?
No. Lifted means may contain bottom. Boxed means represented by a pointer. `ByteArray#` is boxed but unlifted See https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TypeType#Classifyi... On Saturday, February 6, 2016 at 8:54:50 PM UTC+5, Imants Cekusins wrote:
in addition to Takenobu's links, Real World Haskell explains unboxing and lifting on p.583
just to clarify, for practical use, is it safe to say that "boxed" and "lifted" are synonyms? you see, term "boxed" is used in other languages. I assumed "lifting" related to monads. Hence the confusion. _______________________________________________ Haskell-Cafe mailing list Haskel...@haskell.org javascript: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Hi Imants and cafe,
Related informal illustrations (and references) are here:
Lifted/unlifted, boxed/unboxed types
https://takenobu-hs.github.io/downloads/haskell_lazy_evaluation.pdf#page=182
Bottom
https://takenobu-hs.github.io/downloads/haskell_lazy_evaluation.pdf#page=164
Regards,
Takenobu
2016-02-07 0:54 GMT+09:00 Imants Cekusins
in addition to Takenobu's links, Real World Haskell explains unboxing and lifting on p.583
just to clarify, for practical use, is it safe to say that "boxed" and "lifted" are synonyms? you see, term "boxed" is used in other languages. I assumed "lifting" related to monads. Hence the confusion. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Thank you very much Takenobu, these are very clear and extensive explanations.

On 7 Feb 2016, at 2:32 am, Michał Antkiewicz
wrote: Every programmer should understand the difference between boxed and unboxed values. Period. The fact that Haskell allows for levity polimorphism is something we should be proud of and leverage in teaching, not hide it or lie about it.
When I first read the “Unboxed values as first class citizens..” paper I felt smart, like learning the difference between Int and Int# was helping me understand how compilers worked under the hood. Now, ~15 years later, I already know all these details and having to constantly worry about the difference between Int and Int# when writing application code makes me feel dumb. When I then read people praising the advanced Haskell type system and the fact that we can now use advanced Haskell type system features to write polymorphic functions that work with both Int and Int# I feel even dumber. This isn’t a criticism of the work on levity polymorphism, more that it feels to me like a solution to a problem that should not exist in the first place. In my own mind, the boxed/unboxed problem is in the same bucket as the problem that ownership typing tries to address in the OO world. Every OO programmer can recite the great engineering benefits of splitting the fields and methods of an object into public and private. However, in most OO languages, if a public method returns a reference to mutable private state, then the caller can then update the internal state of the object. Ownership typing is an advanced type system extension for OO languages to prevent this from happening. It also feels like the same class of problem as what the segmentation registers in the x86 architecture try to address. Does anyone here know how the 8086 processor addresses more than 64k of memory with only 16-bit address registers? I sure wish I didn’t. As a budding young programmer, reading about these details was a way to learn how computers operate, but it’s not information I would inflict on any of my current students. With Int/Int#, public/private and x86 segmentation, the abstraction never really worked in the first place. However, because there weren’t any other obvious solutions at the time, these “features” were grandfathered into the culture of the technology. For the disciples of a particular technology, the recognition of problems in the fundamental design of that technology tends to result in extensions to what they already have -- rather than backing up and reconsidering WTF they were thinking in the first place. These days when I read papers about advanced type system extensions I typically feel smart for a few days, but then after reflecting on it a few weeks or months later I often feel dumb again. I’m not sure if that’s something to be proud of... Ben.

1, Just to clarify, there are no actual known uses for a TypeRep-polymorphic ($), right? I thought I saw someone say that. 2, ($) has had a fib in its type for a very long time, but did it ever hurt anyone? The closest I saw was a generalized concern about it being bad when people report a bug and then hear that things are more general than they thought and I guess this makes their bug not a bug or something? Results in some confusing back-and-forth? It would be nice to get more specific about how much trouble the ($) lie has caused. 3, Is anything other than ($) TypeRep-polymorphic? If 2 is "not very much", an obvious solution is to keep lying and not worry about it. But clearly someone had a reason strong enough to make this change... what was it? If 1 is "maybe not" then an obvious solution is to just make ($) not TypeRep-polymorphic. I can't imagine that not being able to use ($) is a serious problem if you're working with # types, given that there are already tons of other restrictions. I hesitate to get distracted from the specifics, but I agree with Ben Lippemeier in that I like how haskell doesn't make me worry about boxed vs. unboxed. I'm not saying one little type annotation on one operator is suddenly making me worry since I've never even thought to run :t ($). But I'm happy that # is mostly off in its own little corner and you don't even have to know it exists until you need to get into low level optimization.

On Feb 7, 2016, at 4:22 AM, Evan Laforge
1, Just to clarify, there are no actual known uses for a TypeRep-polymorphic ($), right? I thought I saw someone say that.
Here is where this treatment of ($) was introduced: https://ghc.haskell.org/trac/ghc/ticket/8739
2, ($) has had a fib in its type for a very long time, but did it ever hurt anyone? The closest I saw was a generalized concern about it being bad when people report a bug and then hear that things are more general than they thought and I guess this makes their bug not a bug or something? Results in some confusing back-and-forth? It would be nice to get more specific about how much trouble the ($) lie has caused.
I don't have data, but there is a real cost to lying. It shows up in the slow-ish but steady stream of posts / questions / bug reports that are produced saying something is weird. I've seen a good number of these come up in my years in the Haskell community. I'll note that there is also a real cost to telling the truth: witness this thread. This all adds up to a need to do both, which is what we would get by having a richer REPL environment.
3, Is anything other than ($) TypeRep-polymorphic?
Yes: undefined and error. These really are used at unlifted types.
If 2 is "not very much", an obvious solution is to keep lying and not worry about it. But clearly someone had a reason strong enough to make this change... what was it?
The change to the user-visible type of ($) is due to updates around TypeInType. But ($)'s ability to handle this case has been around since 7.8. Note that if you say :i $ in GHCi 7.10 you get a mention of OpenKind, which is the ancestor of current representation-polymorphism. It's just that :t ($) did a nice job of hiding it.
I hesitate to get distracted from the specifics, but I agree with Ben Lippemeier in that I like how haskell doesn't make me worry about boxed vs. unboxed. I'm not saying one little type annotation on one operator is suddenly making me worry since I've never even thought to run :t ($). But I'm happy that # is mostly off in its own little corner and you don't even have to know it exists until you need to get into low level optimization.
And this is what the new -fshow-runtime-rep flag will (re-)enable. Richard

On 7 Feb 2016, at 2:59 pm, Richard Eisenberg
wrote: 2, ($) has had a fib in its type for a very long time, but did it ever hurt anyone? The closest I saw was a generalized concern about it being bad when people report a bug and then hear that things are more general than they thought and I guess this makes their bug not a bug or something? Results in some confusing back-and-forth? It would be nice to get more specific about how much trouble the ($) lie has caused.
I don't have data, but there is a real cost to lying. It shows up in the slow-ish but steady stream of posts / questions / bug reports that are produced saying something is weird. I've seen a good number of these come up in my years in the Haskell community. I'll note that there is also a real cost to telling the truth: witness this thread. This all adds up to a need to do both, which is what we would get by having a richer REPL environment.
This reminds a lot of the FTP controversy and I feel that here too we could side step the problem by allowing modules to re-export type-specialized versions of imported symbols. E.g., Data.Function could define ($) with the new type, Prelude would re-export it with the old type (as a specialization) and anyone who needs to use the more general version would have to opt-in by importing Data.Function. In terms of documentation, the haddock version of (Data.Function.$) would show the more general type while the haddock for Prelude would show the current type (ideally with a link to the more general one).

On Sun, Feb 07, 2016 at 03:56:25PM +0000, Daniel Gorín wrote:
On 7 Feb 2016, at 2:59 pm, Richard Eisenberg
wrote: 2, ($) has had a fib in its type for a very long time, but did it ever hurt anyone? The closest I saw was a generalized concern about it being bad when people report a bug and then hear that things are more general than they thought and I guess this makes their bug not a bug or something? Results in some confusing back-and-forth? It would be nice to get more specific about how much trouble the ($) lie has caused.
I don't have data, but there is a real cost to lying. It shows up in the slow-ish but steady stream of posts / questions / bug reports that are produced saying something is weird. I've seen a good number of these come up in my years in the Haskell community. I'll note that there is also a real cost to telling the truth: witness this thread. This all adds up to a need to do both, which is what we would get by having a richer REPL environment.
[..]
Data.Function could define ($) with the new type, Prelude would re-export it with the old type (as a specialization) [..]
Could you explain why re-exporting a specialized version is better than just *defining* a specialized equivalent? Tom

[..]
Data.Function could define ($) with the new type, Prelude would re-export it with the old type (as a specialization) [..]
Could you explain why re-exporting a specialized version is better than just *defining* a specialized equivalent?
I guess that for the same reasons it was considered better not to have duplicated definitions in Data.List of the more general functions in Data.Foldable? I’m just saying this seems to be another instance of a recurrent problem that we had before and that we’ll probably face again in the future.

I'm curious... Ultimately, ($) is just a name for what is otherwise unnameable: the whitespace which means application. However, application whitespace is a bit funny since it works uniformly for mono-/polymorphic arguments, un/boxed arguments, functions/record fields, etc— which is why we keep running into issues with typing ($). So my curiosity is this: why do we insist on considering ($) to be a function in the language rather than being syntax? We have overt syntax for other forms of whitespace, namely to deal with blocks and indentation, and we don't worry about what their types are, so why not treat ($) similarly? Sure, there are higher-order uses of ($), as when people write things like fmap($x), but afaict none of our typing hacks are worried about continuing to work in those settings, so there's no particular reason to think that those uses of a higher-order function capturing function application should be considered identical to the ($) used with runST, Int#, etc. -- Live well, ~wren

On 02/07/2016 06:23 PM, wren romano wrote:
I'm curious...
Ultimately, ($) is just a name for what is otherwise unnameable: the whitespace which means application. However, application whitespace is a bit funny since it works uniformly for mono-/polymorphic arguments, un/boxed arguments, functions/record fields, etc— which is why we keep running into issues with typing ($).
I like the new type signature. It indicates that you're about to invoke dark magic line noise to avoid something simple and well-understood and in use since the beginning of time (parentheses).

what if use of ($) were confined to boxed & lifted types only? use (...) for unboxed / unlifted types. would this not simplify the issue somewhat?

On 8/02/16 1:42 pm, Michael Orlitzky wrote:
I like the new type signature. It indicates that you're about to invoke dark magic line noise to avoid something simple and well-understood and in use since the beginning of time (parentheses). So time began in the 15th century? No, wait, that's text. For mathematics, time must have begun in the 16th century. http://jeff560.tripod.com/grouping.html
Wait, those were grouping parentheses, not application ones. According to https://en.wikipedia.org/wiki/History_of_the_function_concept something explicit resembling modern ideas of a "function" appeared in the 17th century, so THAT's when time began. I always wondered.

On 02/08/2016 06:54 PM, Richard A. O'Keefe wrote:
Wait, those were grouping parentheses, not application ones. According to https://en.wikipedia.org/wiki/History_of_the_function_concept something explicit resembling modern ideas of a "function" appeared in the 17th century, so THAT's when time began.
I always wondered.
I was referring to grouping parentheses. Most uses of "$" are for stupid things like "sin $ 1 + 2" where parentheses would be much more clear. By "beginning of time" I thought it was clear I meant "I don't feel like looking it up," but thanks -- we now know that people have been using parentheses about 400 years longer than "$" for grouping.

Michael Orlitzky
Most uses of "$" are for stupid things like "sin $ 1 + 2" where parentheses would be much more clear.
"$" simplifies visual perception through two factors: 1. we are relieved from counting parentheses 2. it serves as a cue to treat the entire remaining part until ")" as part of the same expression Case in point (only slightly contrived) -- which one is easier to visually parse to you: foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp) foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp))) Me, I get a blood pressure spike roughy here ---^ -- с уважениeм / respectfully, Косырев Сергей

As for me, I don't like ubiquitous $ everywhere, often I prefer parentheses over $ (this might be due to my Elisp practices), and I like very much dot style. Most uses of "$" are for stupid things like "sin $ 1 + 2" where parentheses would be much more clear. Agree. "$" simplifies visual perception through two factors: I think this does not hold for every single person. 1. we are relieved from counting parentheses This is not big deal if your editor/IDE can highlight parentheses and even better provide commands to jump to opening/closing parenthesis. foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp) foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp))) Me, I get a blood pressure spike roughy here ---^ Personally, I find hard to read expressions like: someFn $ someOtherFn $ more $ more $ val (someFn . someOtherFn . more . more $ val) seems better for me, but this is also good: someFn (someOtherFn (more (more val))) Parentheses annoys me only when I need to wrap something by parentheses in Emacs with electric-pairs-mode :D

Am 09.02.2016 um 12:27 schrieb Kosyrev Serge:
Case in point (only slightly contrived) -- which one is easier to visually parse to you:
foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp)
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
You can always denest by naming subexpressions. e.g. let subexpr = thInt (fromIntegral (c2hsValueInt cexp))) in foo subexpr subexpr or let subexpr = (thInt . fromIntegral . c2hsValueInt) cexp in foo subexpr subexpr or let fn = thInt . fromIntegral . c2hsValueInt in foo (fn cexp) (fn cexp) I routinely do that kind of denesting as soon as a line goes beyond 72 characters. I have found that such code is usually far easier to read regardless of what I'm doing in the line. I think the problem is not that you cannot write readable code, it is that people do not use existing facilities for that. If that's correct, then adding another facility for the same purpose is unlikely to help.

On 02/09/2016 06:27 AM, Kosyrev Serge wrote:
Case in point (only slightly contrived) -- which one is easier to visually parse to you:
foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp)
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
Me, I get a blood pressure spike roughy here ---^
let th_exp = (thInt . fromIntegral . c2hsValueInt) cexp foo th_exp th_exp The parentheses are a warning sign, and using "$" above only lets you make it look cleaner without fixing the problem. It has hidden the fact that you're computing f(g(h(x))) twice, and there's a better way to do that.

On Tue, Feb 09, 2016 at 02:27:44PM +0300, Kosyrev Serge wrote:
Michael Orlitzky
writes: Most uses of "$" are for stupid things like "sin $ 1 + 2" where parentheses would be much more clear.
"$" simplifies visual perception through two factors:
1. we are relieved from counting parentheses 2. it serves as a cue to treat the entire remaining part until ")" as part of the same expression
Case in point (only slightly contrived) -- which one is easier to visually parse to you:
foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp)
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
My readability problem with this statement is line length. How about: foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp))) I apologize for playing syntax golf, but I do want to cast a small vote for preferring parentheses over ($). It is this preference that makes me side with Orlitzky's argument that the type of ($) is irrelevant for beginners, since beginners should be encouraged to use parentheses anyways. It's One Less Thing To Worry About(tm).

Bryan Richter writes:
On Tue, Feb 09, 2016 at 02:27:44PM +0300, Kosyrev Serge wrote:
Michael Orlitzky
writes: Most uses of "$" are for stupid things like "sin $ 1 + 2" where parentheses would be much more clear.
"$" simplifies visual perception through two factors:
1. we are relieved from counting parentheses 2. it serves as a cue to treat the entire remaining part until ")" as part of the same expression
Case in point (only slightly contrived) -- which one is easier to visually parse to you:
foo (thInt $ fromIntegral $ c2hsValueInt cexp) (thInt $ fromIntegral $ c2hsValueInt cexp)
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
My readability problem with this statement is line length. How about:
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
I clearly made a mistake of duplicating a real expression.. should have picked two different expressions for an example. The point I have tried to convey was that: - the ))) ( patterns are hard to parse visually - the $ helps to alleviate that ..which is fairly orthogonal to common subexpression elimination or formatting. $ allows one to write more complex expressions -- precisely without resorting to diluting code with formatting. -- с уважениeм / respectfully, Косырев Сергей

The point I have tried to convey was that:
- the ))) ( patterns are hard to parse visually - the $ helps to alleviate that
..which is fairly orthogonal to common subexpression elimination or formatting.
$ allows one to write more complex expressions -- precisely without resorting to diluting code with formatting.
I agree, but '$' still needs to be used with a bit of care. For instance, given these choices:
someFn $ someOtherFn $ more $ more $ val (someFn . someOtherFn . more . more $ val) someFn (someOtherFn (more (more val)))
I like one not on the list: someFn . someOtherFn . more $ more val as it's simpler in that there are fewer things to parse than the other options. The middle option in the first three without the extra global '()'s is close, and might even be better in context once they are removed, but the last one makes me long for he old LISP super-parens, even though I thought and still think they were a bad idea. So my rule of thumb is one '$' for expression or sub-expression, using '.' instead of '$' prior to that to make the distinction between building a functional value and applying it stand out.

Am 09.02.2016 um 18:24 schrieb Kosyrev Serge:
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
I clearly made a mistake of duplicating a real expression.. should have picked two different expressions for an example.
The counterexamples still work. This: foo (thInt1 (fromIntegral1 (c2hsValueInt1 cexp1))) (thInt2 (fromIntegral2 (c2hsValueInt2 cexp2))) can still become this: let int1 = thInt1 (fromIntegral1 (c2hsValueInt1 cexp1)) int2 = thInt2 (fromIntegral2 (c2hsValueInt2 cexp2)) in foo int1 int2 and that's perfectly readable in my book. If you don't like the nested parentheses, use function composition: let fn1 = thInt1 . fromIntegral1 . c2hsValueInt1 fn2 = thInt2 . fromIntegral2 . c2hsValueInt2 in foo (fn1 int1) (fn2 int2) Function composition isn't the main tool though; I found that naming subexpressions always works, plus the names can help with readability if they are chosen wisely.

I think that's the reason why Python doesn't have multi-line lambdas: Guido believed they aren't readable enough without giving them names. It's wrong for exactly the same reasons.
On 09 Feb 2016, at 19:11, Joachim Durchholz
wrote: Am 09.02.2016 um 18:24 schrieb Kosyrev Serge:
foo (thInt (fromIntegral (c2hsValueInt cexp))) (thInt (fromIntegral (c2hsValueInt cexp)))
I clearly made a mistake of duplicating a real expression.. should have picked two different expressions for an example.
The counterexamples still work.
This:
foo (thInt1 (fromIntegral1 (c2hsValueInt1 cexp1))) (thInt2 (fromIntegral2 (c2hsValueInt2 cexp2)))
can still become this:
let int1 = thInt1 (fromIntegral1 (c2hsValueInt1 cexp1)) int2 = thInt2 (fromIntegral2 (c2hsValueInt2 cexp2)) in foo int1 int2
and that's perfectly readable in my book.
If you don't like the nested parentheses, use function composition:
let fn1 = thInt1 . fromIntegral1 . c2hsValueInt1 fn2 = thInt2 . fromIntegral2 . c2hsValueInt2 in foo (fn1 int1) (fn2 int2)
Function composition isn't the main tool though; I found that naming subexpressions always works, plus the names can help with readability if they are chosen wisely. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Mon, Feb 8, 2016 at 4:53 AM, wren romano
I'm curious...
Ultimately, ($) is just a name for what is otherwise unnameable: the whitespace which means application. However, application whitespace is a bit funny since…
A view from the other side: 1. Delete the unnameable 2. First-class application (its not called '$' but '.' -- a minor difference) 3. Remove all specialness of it http://blog.languager.org/2014/09/pugofer.html With justifications here: http://www.the-magus.in/Publications/ewd.pdf

On 02/08/2016 11:13 AM, Marcin Mrotek wrote:
1. Delete the unnameable 3. Remove all specialness of it
... and end up with absolutely no way to apply functions that return unboxed values?
I think the point is that we don't need to worry about what the type of " " is in the expression "f x", because it's syntax for function application. If we had /explicit/ syntax for function application (read the PDF, it's good), there would be no problem to begin with -- syntax isn't typed. The suggestion in the PDF is basically to drop the "f x" syntax and always use "f $ x" which has a lot of merit if you rename "$" to something less ugly and more obvious. The paper proposes "f.x", but you could also equate "f." with "f()" to make something crazy like "f(x)" work.

Am 08.02.2016 um 17:32 schrieb Michael Orlitzky:
The suggestion in the PDF is basically to drop the "f x" syntax and always use "f $ x" which has a lot of merit if you rename "$" to something less ugly and more obvious.
I'm wondering what the merit would be.

On 02/08/2016 11:45 AM, Joachim Durchholz wrote:
Am 08.02.2016 um 17:32 schrieb Michael Orlitzky:
The suggestion in the PDF is basically to drop the "f x" syntax and always use "f $ x" which has a lot of merit if you rename "$" to something less ugly and more obvious.
I'm wondering what the merit would be.
By making "f $ x" or more generally "f $" syntax, we avoid the very issue that sparked this thread (that's what wren suggested...). We also no longer need the voodoo hacks for things like runST. It's less confusing for the parser and for students (explicit is better than implicit), etc.

Am 08.02.2016 um 17:56 schrieb Michael Orlitzky:
On 02/08/2016 11:45 AM, Joachim Durchholz wrote:
Am 08.02.2016 um 17:32 schrieb Michael Orlitzky:
The suggestion in the PDF is basically to drop the "f x" syntax and always use "f $ x" which has a lot of merit if you rename "$" to something less ugly and more obvious.
I'm wondering what the merit would be.
By making "f $ x" or more generally "f $" syntax, we avoid the very issue that sparked this thread (that's what wren suggested...).
Wouldn't the type of function application be independent of its syntax?
We also no longer need the voodoo hacks for things like runST.
I can't comment on runST (my Haskell knowledge is really basic). But if there's a problem because juxtaposition has no representation as an operator symbol or function name, it should be easier to fix the syntactic problem than to rewrite the type system. Of course, if the type system got warts because of the existence of juxtaposition, then that should be fixed.
It's less confusing for the parser and for students (explicit is better than implicit), etc.
Sure, but you don't make that an absolute. Otherwise you'd have to remove operator precedence, too. And the end result would look like Lisp. juxtaposition is so entrenched in almost all branches of math that using that implicitness to reduce syntactic overhead is a net win.

On 02/08/2016 12:15 PM, Joachim Durchholz wrote:
Sure, but you don't make that an absolute. Otherwise you'd have to remove operator precedence, too. And the end result would look like Lisp.
juxtaposition is so entrenched in almost all branches of math that using that implicitness to reduce syntactic overhead is a net win.
I don't think anyone was seriously suggesting getting rid of "f x" for function application. Rustom's post is interesting because it suggests that if you're going to have only one of "f x" or "f $ x", then it should be the latter. It's a fun thought experiment. Since we're stuck with "f x", the question is: do we want *both* as syntax? If most uses of "$" are for, putStrLn $ "Hello" ++ " world!" to avoid, putStrLn ("Hello" ++ " world!") then I think it's silly to worry about the type of "$". Most people don't have to know, care, or use it -- the second example is much clearer. And if instead you're doing some kind of fmap (((f $) $) $) gymnastics, then you probably don't mind the type of "$".

Am 08.02.2016 um 18:59 schrieb Michael Orlitzky:
If most uses of "$" are for,
putStrLn $ "Hello" ++ " world!"
to avoid,
putStrLn ("Hello" ++ " world!")
then I think it's silly to worry about the type of "$". Most people don't have to know, care, or use it -- the second example is much clearer.
Only if you are on the standard programming language mindset. I.e. those who learn Haskell first will find putStrLn "Hello" ++ "world!" more natural.

Are you seriously suggesting to overloaded semicolons to *avoid*
confusing newcomers to Haskell from other languages?
And all of that just in case that newcomer happens to look at the
output of :t ($)?
On Mon, Feb 8, 2016 at 8:28 PM, Joachim Durchholz
Am 08.02.2016 um 18:59 schrieb Michael Orlitzky:
If most uses of "$" are for,
putStrLn $ "Hello" ++ " world!"
to avoid,
putStrLn ("Hello" ++ " world!")
then I think it's silly to worry about the type of "$". Most people don't have to know, care, or use it -- the second example is much clearer.
Only if you are on the standard programming language mindset. I.e. those who learn Haskell first will find putStrLn "Hello" ++ "world!" more natural.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I'm not seeing a semicolon anywhere here... Am 08.02.2016 um 21:39 schrieb Matthias Hörmann:
Are you seriously suggesting to overloaded semicolons to *avoid* confusing newcomers to Haskell from other languages?
And all of that just in case that newcomer happens to look at the output of :t ($)?
On Mon, Feb 8, 2016 at 8:28 PM, Joachim Durchholz
wrote: Am 08.02.2016 um 18:59 schrieb Michael Orlitzky:
If most uses of "$" are for,
putStrLn $ "Hello" ++ " world!"
to avoid,
putStrLn ("Hello" ++ " world!")
then I think it's silly to worry about the type of "$". Most people don't have to know, care, or use it -- the second example is much clearer.
Only if you are on the standard programming language mindset. I.e. those who learn Haskell first will find putStrLn "Hello" ++ "world!" more natural.
_______________________________________________ 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

On Feb 8, 2016, at 10:13, Marcin Mrotek
... and end up with absolutely no way to apply functions that return unboxed values?
Besides juxtaposition, could you not make another operator (e.g. "$#") for this purpose? --Will

would f 1 ; 2 + 3 look ok instead of f 1 $ 2 + 3 ? the semicolon would be part of syntax, not something with a type the intuition is: pause application until the rest is applied

On 02/08/2016 12:33 PM, Imants Cekusins wrote:
would f 1 ; 2 + 3
look ok instead of f 1 $ 2 + 3
?
the semicolon would be part of syntax, not something with a type the intuition is: pause application until the rest is applied
The semicolon is already appropriated sadly: ghci> do { putStr "Hello" ; putStrLn " world!"; } Hello world!

The semicolon is already appropriated sadly:
ghci> do { putStr "Hello" ; putStrLn " world!"; } is it only used for this purpose within braces? would it be a bad taste to overload ;? there are examples: dot, parentheses even have type in their moonlighting capacity: Prelude> :t () () :: ()

On Mon, Feb 8, 2016 at 1:01 PM, Imants Cekusins
would it be a bad taste to overload ;?
Won't it create ambiguity problems like the following? ghci> do { putStr "Hello, the answer is "; print $ 40 + 2 } being changed into the invalid ghci> do { putStr "Hello, the answer is "; print ; 40 + 2 } -- Mihai Maruseac (MM) "If you can't solve a problem, then there's an easier problem you can solve: find it." -- George Polya

Won't it create ambiguity problems...?
we could set simple criteria for "; " a) within code block - keep the current meaning : new statement b) elsewhere: pause application ($) $ would keep its current meaning and the new "honest, confusing" type. Still be usable, would not break the code. ; may mark the code visually better than $ e.g. braces are overloaded: code block, data record

a) within { code block 1; code block 2; } - keep the current meaning : new statement

ghci> do { putStr "Hello, the answer is "; print (40 + 2); } reasonably easy to remember rule of thumb: using braces? use parentheses, too!

On 07/02/2016, wren romano
Ultimately, ($) is just a name for what is otherwise unnameable: the whitespace which means application.
It is in fact otherwise called "id" ☺
So my curiosity is this: why do we insist on considering ($) to be a function in the language rather than being syntax? We have overt syntax for other forms of whitespace, namely to deal with blocks and indentation, and we don't worry about what their types are, so why not treat ($) similarly?
These other symbols, the block delimiters, are not terms, and so have no types.

Am 07.02.2016 um 09:26 schrieb Ben Lippmeier:
It also feels like the same class of problem as what the segmentation registers in the x86 architecture try to address. Does anyone here know how the 8086 processor addresses more than 64k of memory with only 16-bit address registers? I sure wish I didn’t.
Yes I do, and yes I wish I didn't either. For the Int/Int# concept, the approaches I have seen either ignore the efficiency and let the machine figure out what to do (Smalltalk, Python, pre-Int# Haskell), or they complicate the type system at the expense of polymorphism (Java, Eiffel), or they complicate the type system even more to regain some form of polymorphism (C++, today's Haskell). I guess the world is still waiting for an approach that does not force this choice on language designers. Aside note: My own choice would be to have an annotation that tells the compiler to keep something unboxed if it can, and if it cannot, have it print a warning why not. Not seeing this choice in the wild means that either language designers didn't find the right way to do this, or the idea as such is dumb; I don't know which.
With Int/Int#, public/private and x86 segmentation, the abstraction never really worked in the first place. However, because there weren’t any other obvious solutions at the time, these “features” were grandfathered into the culture of the technology.
As much as I agree that design misdecisions can perpetuate by becoming part of the technology culture (see PHP's view on security, or C's insistence on microoptimization), this did not happen for x86 16-bit segment/offset addressing. Even Microsoft switched as fast as they could, and that was in the old days when features were far more important than security or even stability.
For the disciples of a particular technology, the recognition of problems in the fundamental design of that technology tends to result in extensions to what they already have -- rather than backing up and reconsidering WTF they were thinking in the first place.
Word. Problem is that it is possible to add features to existing language, but almost impossible to remove them. Those working on practically useful type systems want to solve an existing problem in an existing language, so they don't have a motive to reconsider; even if they did, they'd quickly drop the thought because whatever the change they'd want, it would get rejected because it would break existing code left and right. This seems to be a universal problem. Every language that I know has it, including Haskell (which I don't really know well enough but the discussion and options are just as with any other language). Regards, Jo

On 7 Feb 2016, at 9:50 pm, Joachim Durchholz
wrote:
For the Int/Int# concept, the approaches I have seen either ignore the efficiency and let the machine figure out what to do (Smalltalk, Python, pre-Int# Haskell), or they complicate the type system at the expense of polymorphism (Java, Eiffel), or they complicate the type system even more to regain some form of polymorphism (C++, today's Haskell).
Although I haven’t implemented it, I suspect another approach is to just specialise every polymorphic function at its unboxed type arguments. Boxed and unboxed value types would share the same kind. Of course, full specialisation of polymorphic code assumes that code is available in the interface files, but we’ve almost got that already. Dealing with mutual recursion could be a pain, though. I don’t think specialisation was an option back when unboxed types were originally implemented. I believe GHC’s support for cross module inlining came some time after the unboxed types, if the publication dates of the relative papers are to be a guide. Ben.

This doesn't really work in a non-strict language like Haskell with
uncontrolled recursion. We often need a lazy int that may be _|_ and
shouldn't affect termination of the program unless demanded.
The result would be that you'd actually have to compile all of your code
several ways times the number of type arguments and you'd get rather
severely different semantics around evaluation as it switched between
strictness and laziness.
Moreover, cycles that happened to involve one of these values would have to
tie the knot strictly, meaning you'd have issues like scheme where letrec
secretly exposes extra, observable, #f cases when you encounter a cycle.
-Edward
On Sun, Feb 7, 2016 at 7:17 AM, Ben Lippmeier
On 7 Feb 2016, at 9:50 pm, Joachim Durchholz
wrote: For the Int/Int# concept, the approaches I have seen either ignore the efficiency and let the machine figure out what to do (Smalltalk, Python, pre-Int# Haskell), or they complicate the type system at the expense of polymorphism (Java, Eiffel), or they complicate the type system even more to regain some form of polymorphism (C++, today's Haskell).
Although I haven’t implemented it, I suspect another approach is to just specialise every polymorphic function at its unboxed type arguments. Boxed and unboxed value types would share the same kind. Of course, full specialisation of polymorphic code assumes that code is available in the interface files, but we’ve almost got that already. Dealing with mutual recursion could be a pain, though.
I don’t think specialisation was an option back when unboxed types were originally implemented. I believe GHC’s support for cross module inlining came some time after the unboxed types, if the publication dates of the relative papers are to be a guide.
Ben.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I meant to retain the separate types Int and Int#, but to assign both of them the same kind *. I believe the unboxed kind # was added to prevent polymorphic functions being instantiated at unboxed types. This is done because while values with types of kind * have a uniform representation, values with types of kind # do not, so the usual code generation approach does not work. The solution I was proposing was to allow polymorphic functions to be used at unboxed types, and do the code generation by simply specialising those functions for the types they are instantiated at. You would need to compile each polymorphic function several times, but then if someone is writing code that uses explicit unboxed types the associated functions are probably INLINEd anyway. The Int type would still be lazy/lifted, and Int# unlifted, so that would not have to change. If you think the above plan would not work then I’d like to hear about it. I was planning to do something like it for DDC (which still progresses..) Ben.
On 9 Feb 2016, at 4:39 am, Edward Kmett
wrote: This doesn't really work in a non-strict language like Haskell with uncontrolled recursion. We often need a lazy int that may be _|_ and shouldn't affect termination of the program unless demanded.
The result would be that you'd actually have to compile all of your code several ways times the number of type arguments and you'd get rather severely different semantics around evaluation as it switched between strictness and laziness.
Moreover, cycles that happened to involve one of these values would have to tie the knot strictly, meaning you'd have issues like scheme where letrec secretly exposes extra, observable, #f cases when you encounter a cycle.
-Edward
On Sun, Feb 7, 2016 at 7:17 AM, Ben Lippmeier
mailto:benl@ouroborus.net> wrote: On 7 Feb 2016, at 9:50 pm, Joachim Durchholz
mailto:jo@durchholz.org> wrote: For the Int/Int# concept, the approaches I have seen either ignore the efficiency and let the machine figure out what to do (Smalltalk, Python, pre-Int# Haskell), or they complicate the type system at the expense of polymorphism (Java, Eiffel), or they complicate the type system even more to regain some form of polymorphism (C++, today's Haskell).
Although I haven’t implemented it, I suspect another approach is to just specialise every polymorphic function at its unboxed type arguments. Boxed and unboxed value types would share the same kind. Of course, full specialisation of polymorphic code assumes that code is available in the interface files, but we’ve almost got that already. Dealing with mutual recursion could be a pain, though.
I don’t think specialisation was an option back when unboxed types were originally implemented. I believe GHC’s support for cross module inlining came some time after the unboxed types, if the publication dates of the relative papers are to be a guide.
Ben.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org mailto:Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 06/02/2016, Michał Antkiewicz
Every programmer should understand the difference between boxed and unboxed values. Period.
Why? The performance of some code is not critical. Sometimes i need not care about 1 more indirection in their code, and some authors need never care, and in these cases i appreciate Haskell not forcing me to care. I'm not saying every language ought to hide this – i quite like Rust, for example – but most times hiding it works quite well for me in Haskell.

Hi,
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.
[1] http://takenobu-hs.github.io/downloads/type_introduction_illustrated.pdf
Regards,
Takenobu
2016-02-06 9:09 GMT+09:00 Richard Eisenberg
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 (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
On Feb 5, 2016, at 6:30 PM, George Colpitts
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
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
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
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
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

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.
I think a low-hanging fruit would be to add the ability for Haddock to parse some sort of specialized types annotation (could be entirely in the comments) and display them adjacent to the true type. The types do have to be manually written, but at least they can be type-checked. Lens does this in its documentation and they are very helpful for learning the library.
(^.) :: s -> Getting a s a -> a (^.) :: s -> Getter s a -> a (^.) :: Monoid m => s -> Fold s m -> m (^.) :: s -> Iso' s a -> a (^.) :: s -> Lens' s a -> a (^.) :: Monoid m => s -> Traversal' s m -> m
participants (27)
-
Alexey Vagarenko
-
Artyom
-
Ben Lippmeier
-
Bryan Richter
-
Daniel Gorín
-
Edward Kmett
-
Evan Laforge
-
Geraldus
-
Imants Cekusins
-
Joachim Durchholz
-
Kosyrev Serge
-
M Farkas-Dyck
-
Marcin Mrotek
-
Matthias Hörmann
-
Michael Orlitzky
-
Michał Antkiewicz
-
MigMit
-
Mihai Maruseac
-
Mike Meyer
-
Phil Ruffwind
-
Richard A. O'Keefe
-
Richard Eisenberg
-
Rustom Mody
-
Takenobu Tani
-
Tom Ellis
-
Will Yager
-
wren romano