an idea for modifiyng data/newtype syntax: use `::=` instead of `=`

Hello, i would like to suggest an idea for modifying the basic data/newtype syntax in Haskell: replace the equality sign `=` with `::=`. When i started learning Haskell, the most confusing part of the syntax for me was the equality sign in `data` definition. I could not even guess what the `data` definition meant without reading a chapter or two about types in Haskell, and i think it was partially due to the equality sign. I still find this notation inconsistent with other uses of the equality sign in Haskell and in general. For example, in type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date the second line is particularly disorienting IMO because on two sides of the equality, `Date` denotes different things. As far as i understand, in all contexts except those of `data` and `newtype` definitions, the equality sign in Haskell denotes the actual equality for all purposes: if a line foo x y = bar y x is present in a program, `foo a b` and `bar b a` can be used more or less interchangeably elsewhere in the program. Similarly, if the line type Name = String is present, `Name` can be used as `String`. Clearly, the equality in data Date = Date Int Int Int does not have such property. I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date IMO this would make the program easier to read in general and the difference between `type` and `newtype` more clear. Maybe the can even make the use of keywords redundant, by allowing to write simply Name = String Date ::= Date Int Int Int Anniversary ::= Birthday Name Date | Wedding Name Name Date What do you think? Alexey.

There is reasonable reasoning here, but what you suggest breaks almost all
Haskell code that has ever been written. Sounds like an idea better suited
to the design of a new language :)
Also, i think you need the newtype/data distinction as I believe they are
operationally different wrt laziness.
ocharles
On Sat, 8 Aug 2015 12:09 pm Alexey Muranov
Hello,
i would like to suggest an idea for modifying the basic data/newtype syntax in Haskell: replace the equality sign `=` with `::=`.
When i started learning Haskell, the most confusing part of the syntax for me was the equality sign in `data` definition. I could not even guess what the `data` definition meant without reading a chapter or two about types in Haskell, and i think it was partially due to the equality sign. I still find this notation inconsistent with other uses of the equality sign in Haskell and in general.
For example, in
type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date
the second line is particularly disorienting IMO because on two sides of the equality, `Date` denotes different things.
As far as i understand, in all contexts except those of `data` and `newtype` definitions, the equality sign in Haskell denotes the actual equality for all purposes: if a line
foo x y = bar y x
is present in a program, `foo a b` and `bar b a` can be used more or less interchangeably elsewhere in the program. Similarly, if the line
type Name = String
is present, `Name` can be used as `String`. Clearly, the equality in
data Date = Date Int Int Int
does not have such property.
I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of
type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date
IMO this would make the program easier to read in general and the difference between `type` and `newtype` more clear. Maybe the can even make the use of keywords redundant, by allowing to write simply
Name = String Date ::= Date Int Int Int Anniversary ::= Birthday Name Date | Wedding Name Name Date
What do you think?
Alexey. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 8 août 2015, at 14:40, Oliver Charles
There is reasonable reasoning here, but what you suggest breaks almost all Haskell code that has ever been written. Sounds like an idea better suited to the design of a new language :)
Maybe adding a language pragma or having both would avoid break any code?
Also, i think you need the newtype/data distinction as I believe they are operationally different wrt laziness.
Maybe keeping the `newtype` keyword would suffice? Alexey.

Hi Alexey,
this is indeed a confusion point for many newcomers, including me.
In Haskell we are being told that the equality sign really means equality
as in "interchangeable", as opposed to "assignment of value" in many other
languages.
So what about the equal sign in data definition? Plus types and type
constructors often have the same name without being the same thing, this
adds to the confusion.
So I really like your suggestion.
However as said Oliver this breaks all existing Haskell code.
One thing I quite don't understand is: do we really need to make the
newtype/data distinction explicit?
If we used only one keyword, it should be quite easy for the compiler to
detect the distinction statically.
On Sat, Aug 8, 2015 at 1:09 PM, Alexey Muranov
Hello,
i would like to suggest an idea for modifying the basic data/newtype syntax in Haskell: replace the equality sign `=` with `::=`.
When i started learning Haskell, the most confusing part of the syntax for me was the equality sign in `data` definition. I could not even guess what the `data` definition meant without reading a chapter or two about types in Haskell, and i think it was partially due to the equality sign. I still find this notation inconsistent with other uses of the equality sign in Haskell and in general.
For example, in
type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date
the second line is particularly disorienting IMO because on two sides of the equality, `Date` denotes different things.
As far as i understand, in all contexts except those of `data` and `newtype` definitions, the equality sign in Haskell denotes the actual equality for all purposes: if a line
foo x y = bar y x
is present in a program, `foo a b` and `bar b a` can be used more or less interchangeably elsewhere in the program. Similarly, if the line
type Name = String
is present, `Name` can be used as `String`. Clearly, the equality in
data Date = Date Int Int Int
does not have such property.
I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of
type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date
IMO this would make the program easier to read in general and the difference between `type` and `newtype` more clear. Maybe the can even make the use of keywords redundant, by allowing to write simply
Name = String Date ::= Date Int Int Int Anniversary ::= Birthday Name Date | Wedding Name Name Date
What do you think?
Alexey. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sat, Aug 08, 2015 at 04:23:15PM +0200, Corentin Dupont wrote:
One thing I quite don't understand is: do we really need to make the newtype/data distinction explicit?
I'm pretty sure we don't. newtype N = N A defines a datatype isomorphic to data D = D !A but you have to use it slightly differently, that's all. I wrote more about this here: http://stackoverflow.com/posts/21331284/revisions Tom

On Sat, Aug 08, 2015 at 03:33:35PM +0100, Tom Ellis wrote:
On Sat, Aug 08, 2015 at 04:23:15PM +0200, Corentin Dupont wrote:
One thing I quite don't understand is: do we really need to make the newtype/data distinction explicit?
I'm pretty sure we don't.
newtype N = N A
defines a datatype isomorphic to
data D = D !A
but you have to use it slightly differently, that's all. I wrote more about this here:
Correction: http://stackoverflow.com/questions/21327740/strict-single-constructor-single... (My previous post linked to the previous revisions of the answer!)

Hi Corentin,
On 8 août 2015, at 17:23, Corentin Dupont
One thing I quite don't understand is: do we really need to make the newtype/data distinction explicit? If we used only one keyword, it should be quite easy for the compiler to detect the distinction statically.
I think someone more knowledgable in Haskell than me should answer this. Alexey.

Hi Alexey,
I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of
type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date
there is already an alternative syntax for declaring datatypes: data Date where Date :: Int -> Int -> Int -> Date data Anniversary where Birthday :: Name -> Date -> Anniversary Wedding :: Name -> Name -> Date -> Anniversary This is called "GADT syntax" and can be enabled with -XGADTSyntax in GHC.* Cheers Lars * The underlying machinery also extends the fragment of admissible datatype declarations beyond what is possible with the standard syntax. See: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/data-type-ex...

Hi, Lars,
On 8 août 2015, at 17:29, Lars Hupel
there is already an alternative syntax for declaring datatypes:
data Date where Date :: Int -> Int -> Int -> Date
data Anniversary where Birthday :: Name -> Date -> Anniversary Wedding :: Name -> Name -> Date -> Anniversary
This is called "GADT syntax" and can be enabled with -XGADTSyntax in GHC.*
Thank you for suggesting GADT, but i already knew about it. IMO this does not make the syntax with the equality sign (the one to which newcomers are introduced first) less weird :). Alexey.

+1
-------- Original Message --------
Subject: Re: [Haskell-cafe] an idea for modifiyng data/newtype syntax: use `::=`
instead of `=`
From: Daniel Trstenjak
Hi Alexey,
type Name = String data Date = Date Int Int Int
if we're at it ;), then please change it to:
alias Name = String type Date = Date Int Int Int
Greetings, Daniel _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

That's ridiculous. The code would only be intuitive enough if it's written like this: típus Név = Sor adat Időpont = Időpont Egész Egész Egész
On 08 Aug 2015, at 17:59, Vlatko Basic
wrote: +1
-------- Original Message -------- Subject: Re: [Haskell-cafe] an idea for modifiyng data/newtype syntax: use `::=` instead of `=` From: Daniel Trstenjak
To: haskell-cafe@haskell.org Date: 08/08/15 17:03 Hi Alexey,
type Name = String data Date = Date Int Int Int
if we're at it ;), then please change it to:
alias Name = String type Date = Date Int Int Int
Greetings, Daniel _______________________________________________ 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 08/08/2015, MigMit
That's ridiculous. The code would only be intuitive enough if it's written like this:
típus Név = Sor adat Időpont = Időpont Egész Egész Egész
+1 behozni Adat.Functor behozni Irányítás.Monad behozni Rendszer.BK -- bemenet-kimenet fő :: BK () fő = tenniSort "Helló, világ!"

On 08/08/2015, MigMit
That's ridiculous. The code would only be intuitive enough if it's written like this:
típus Név = Sor adat Időpont = Időpont Egész Egész Egész
Actually I think you missed the point here: adat Időpont ahol Időpont :: Egész -> Egész -> Egész -> Időpont Much better ☺

Oh, you're right. We need to use Hungarian notation properly. Отправлено с iPad
8 авг. 2015 г., в 18:56, M Farkas-Dyck
написал(а): On 08/08/2015, MigMit
wrote: That's ridiculous. The code would only be intuitive enough if it's written like this: típus Név = Sor adat Időpont = Időpont Egész Egész Egész
Actually I think you missed the point here:
adat Időpont ahol Időpont :: Egész -> Egész -> Egész -> Időpont
Much better ☺

On 08/08/2015 06:17 PM, MigMit wrote:
That's ridiculous. The code would only be intuitive enough if it's written like this:
típus Név = Sor adat Időpont = Időpont Egész Egész Egész
This. The GHC compiler should only accept syntax in this language. Incedenatlly... which language *is* this? ;) I can also summon strange symbols and weird pronunciations at will, but I'm actually really curious about this... My guess is... Hungarian? (If I'm slow on the uptake, I apologize. I have a rather bad cold at the moment.) Regards,

On 8 August 2015 at 08:03, Daniel Trstenjak
type Name = String data Date = Date Int Int Int
if we're at it ;), then please change it to:
alias Name = String type Date = Date Int Int Int
I wholeheartedly agree with replacing the current "type" with "alias", the current "type" is just flat out wrong: it does *not* create a type. This should be very simple to do too: introduce "alias" as a new keyword and deprecate "type". No existing code would be affected. I'm on the fence about "type" instead of "data", though. "data" clearly conveys the meaning that we are talking about data only, not functions on that data (as in OOP). (Well, data constructors are functions too.) Then again, as soon as you add "deriving", you *are* defining functions on that data.

type -> alias
data -> data
newtype -> newalias or newdata?
`type` keyword was really confusing for me at the beginning, also this
confusion brought another one about `newtype` keyword.
сб, 8 авг. 2015 г. в 22:28, Hilco Wijbenga
On 8 August 2015 at 08:03, Daniel Trstenjak
wrote: type Name = String data Date = Date Int Int Int
if we're at it ;), then please change it to:
alias Name = String type Date = Date Int Int Int
I wholeheartedly agree with replacing the current "type" with "alias", the current "type" is just flat out wrong: it does *not* create a type. This should be very simple to do too: introduce "alias" as a new keyword and deprecate "type". No existing code would be affected.
I'm on the fence about "type" instead of "data", though. "data" clearly conveys the meaning that we are talking about data only, not functions on that data (as in OOP). (Well, data constructors are functions too.) Then again, as soon as you add "deriving", you *are* defining functions on that data. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I hate to come across as a party-pooper, but do we really think it's
realistic that any of these changes will be made? I understand that there
may be benefits, but they don't outweigh the cost of breaking almost all
existing code. The idea of opting in to this with pragmas just doesn't seem
worth it, and further fragments the language, ultimately reducing
readability when I pick up another author's code.
*ocharles*
On Sat, Aug 8, 2015 at 7:29 PM Geraldus
type -> alias data -> data newtype -> newalias or newdata?
`type` keyword was really confusing for me at the beginning, also this confusion brought another one about `newtype` keyword.
сб, 8 авг. 2015 г. в 22:28, Hilco Wijbenga
: On 8 August 2015 at 08:03, Daniel Trstenjak
wrote: type Name = String data Date = Date Int Int Int
if we're at it ;), then please change it to:
alias Name = String type Date = Date Int Int Int
I wholeheartedly agree with replacing the current "type" with "alias", the current "type" is just flat out wrong: it does *not* create a type. This should be very simple to do too: introduce "alias" as a new keyword and deprecate "type". No existing code would be affected.
I'm on the fence about "type" instead of "data", though. "data" clearly conveys the meaning that we are talking about data only, not functions on that data (as in OOP). (Well, data constructors are functions too.) Then again, as soon as you add "deriving", you *are* defining functions on that data. _______________________________________________ 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

2015-08-08 21:14 GMT+02:00 Oliver Charles
I hate to come across as a party-pooper, but do we really think it's realistic that any of these changes will be made? I understand that there may be benefits, but they don't outweigh the cost of breaking almost all existing code. The idea of opting in to this with pragmas just doesn't seem worth it, and further fragments the language, ultimately reducing readability when I pick up another author's code.
+1 to this. As has already been noted in a different thread, the "let's introduce a pragma, nobody is forced to use it" argument is bogus. It fragments the language for no good reason and one *is* forced to handle it (when reading other people's code etc.). Furthermore, the current example at hand is roughly at position 2 of Wadler's Law of Language Design ( https://wiki.haskell.org/Wadler's_Law), a.k.a. bikeshedding at the lexical level. ;-) Don't get me wrong: If Haskell was still at its infancy, renaming things would probably be beneficial, as "type" is somehow really a misnomer. One could discuss if "newtype" is really needed when we have "data" plus strictness annotations (personally I'm a bit unsure what the common use case for "case" is where they differ and what one might consider more natural). Given the case that people seem to move towards GADTs anyway, the discussion seems a bit moot. Just my 2c. P.S.: If you think this part of Haskell is a bit hard to read and confusing, I seriously propose a week or two in C++'s wonderful meta-programming world with templates... ;-)

One could discuss if "newtype" is really needed when we have "data" plus strictness annotations (personally I'm a bit unsure what the common use case for "case" is where they differ and what one might consider more natural).
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
P.S.: If you think this part of Haskell is a bit hard to read and confusing, I seriously propose a week or two in C++'s wonderful meta-programming world with templates... ;-)
Can't agree more

On Sat, Aug 08, 2015 at 10:43:39PM +0200, MigMit wrote:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
Sure, they're not exactly the same thing but if you get the translations right you can use them for the same purposes: Prelude> let y = case y of b -> B 1 in y B 1 I list the translations here http://stackoverflow.com/questions/21327740/strict-single-constructor-single... Tom

What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same". That's certainly true, but that "if" is a very big one.
On 08 Aug 2015, at 22:49, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 10:43:39PM +0200, MigMit wrote:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
Sure, they're not exactly the same thing but if you get the translations right you can use them for the same purposes:
Prelude> let y = case y of b -> B 1 in y B 1
I list the translations here
http://stackoverflow.com/questions/21327740/strict-single-constructor-single...
Tom _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sat, Aug 08, 2015 at 11:02:27PM +0200, MigMit wrote:
What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same".
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
On Sat, Aug 08, 2015 at 10:43:39PM +0200, MigMit wrote:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
Sure, they're not exactly the same thing but if you get the translations right you can use them for the same purposes:
Prelude> let y = case y of b -> B 1 in y B 1
I list the translations here
http://stackoverflow.com/questions/21327740/strict-single-constructor-single...

On Sat, Aug 8, 2015 at 5:11 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
When did `data` start guaranteeing that the representation of a single constructor, strict `data' wrapper around another type is exactly the same as the wrapped type? -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sat, Aug 08, 2015 at 05:13:30PM -0400, Brandon Allbery wrote:
On Sat, Aug 8, 2015 at 5:11 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
When did `data` start guaranteeing that the representation of a single constructor, strict `data' wrapper around another type is exactly the same as the wrapped type?
I make no claims about the operational semantics of existing compilers, but can you point out a reason why data of a single strict field *shuoldn't* be compiled in the same way as a newtype?

On Sat, Aug 8, 2015 at 5:16 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Sat, Aug 08, 2015 at 05:13:30PM -0400, Brandon Allbery wrote:
On Sat, Aug 8, 2015 at 5:11 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
When did `data` start guaranteeing that the representation of a single constructor, strict `data' wrapper around another type is exactly the same as the wrapped type?
I make no claims about the operational semantics of existing compilers, but can you point out a reason why data of a single strict field *shuoldn't* be compiled in the same way as a newtype?
Because it's a bizarre special case compared to the way `data` works for everything else? Also, the behavior I described is not something that can just be ignored as an optimization in a particular compiler. It is part of the language specification. In fact, it is the *primary reason* for the existence of `newtype`. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sat, Aug 08, 2015 at 05:20:00PM -0400, Brandon Allbery wrote:
On Sat, Aug 8, 2015 at 5:16 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
On Sat, Aug 08, 2015 at 05:13:30PM -0400, Brandon Allbery wrote:
On Sat, Aug 8, 2015 at 5:11 PM, Tom Ellis < tom-lists-haskell-cafe-2013@jaguarpaw.co.uk> wrote:
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
When did `data` start guaranteeing that the representation of a single constructor, strict `data' wrapper around another type is exactly the same as the wrapped type?
I make no claims about the operational semantics of existing compilers, but can you point out a reason why data of a single strict field *shuoldn't* be compiled in the same way as a newtype?
Because it's a bizarre special case compared to the way `data` works for everything else?
Also, the behavior I described is not something that can just be ignored as an optimization in a particular compiler. It is part of the language specification. In fact, it is the *primary reason* for the existence of `newtype`.
I'm not really sure what we're debating here. I merely pointed out that a language with strict data fields has no less functionality than one that adds newtype. I'm not making any proposal, I'm just making an observation that some may find interesting. Tom

I won't say that's a legitimate concern. Representation doesn't matter here; if those two were indistinguishable (by Haskell code, without "hacks"), then the fact that they are represented differently would be irrelevant. But they ARE distinguishable, by a perfectly normal Haskell code.
On 08 Aug 2015, at 23:13, Brandon Allbery
wrote: On Sat, Aug 8, 2015 at 5:11 PM, Tom Ellis
wrote: No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data. When did `data` start guaranteeing that the representation of a single constructor, strict `data' wrapper around another type is exactly the same as the wrapped type?
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 08 Aug 2015, at 23:11, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:02:27PM +0200, MigMit wrote:
What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same".
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
On Sat, Aug 08, 2015 at 10:43:39PM +0200, MigMit wrote:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
Sure, they're not exactly the same thing but if you get the translations right you can use them for the same purposes:
Prelude> let y = case y of b -> B 1 in y B 1
I list the translations here
http://stackoverflow.com/questions/21327740/strict-single-constructor-single...
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sat, Aug 08, 2015 at 11:14:40PM +0200, MigMit wrote:
On 08 Aug 2015, at 23:11, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:02:27PM +0200, MigMit wrote:
What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same".
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
OK, show me the code!

On 08 Aug 2015, at 23:16, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:14:40PM +0200, MigMit wrote:
On 08 Aug 2015, at 23:11, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:02:27PM +0200, MigMit wrote:
What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same".
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
OK, show me the code!
I did. You removed it when quoting.

On Sat, Aug 08, 2015 at 11:18:18PM +0200, MigMit wrote:
On 08 Aug 2015, at 23:16, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:14:40PM +0200, MigMit wrote:
On 08 Aug 2015, at 23:11, Tom Ellis
wrote: On Sat, Aug 08, 2015 at 11:02:27PM +0200, MigMit wrote:
What you say is "if we don't use some perfectly legitimate language constructs and never use third-party code, than 'data' and 'newtype' are the same".
No, not at all. I'm making a much stronger claim than that. I'm claiming the functionality provided by newtype is completely subsumed by that provided by data.
Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
OK, show me the code!
I did. You removed it when quoting.
Reinstating:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
This isn't third party code. It knows about the constructors of A and B.

Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
OK, show me the code!
I did. You removed it when quoting.
Reinstating:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
This isn't third party code. It knows about the constructors of A and B.
1) This might be the code written by someone using your library/framework. In which case it would know about A and B. 2) It might be generated by the Template Haskell — which is free to use whatever constructor is fed into it.

On Sat, Aug 08, 2015 at 11:32:59PM +0200, MigMit wrote:
Well, it's certainly unsupported by evidence. Because third-party code CAN distinguish between those.
OK, show me the code!
I did. You removed it when quoting.
Reinstating:
Prelude> newtype A = A Int deriving Show Prelude> data B = B !Int deriving Show Prelude> let x = case x of A n -> A 1 in x A 1 Prelude> let y = case y of B n -> B 1 in y *** Exception: <<loop>>
This isn't third party code. It knows about the constructors of A and B.
1) This might be the code written by someone using your library/framework. In which case it would know about A and B.
Then it's up to me to define and document whatever strictness properties I want for my constructors. To reiterate: I'm *not* suggesting replacing newtype A = A a with data A = A! in existing code. Of course that won't work. But for *new* datatypes choosing one rather than the other gives no difference in terms of denotational semantics.
2) It might be generated by the Template Haskell — which is free to use whatever constructor is fed into it.
OK, so show me what goes wrong!

1) This might be the code written by someone using your library/framework. In which case it would know about A and B.
Then it's up to me to define and document whatever strictness properties I want for my constructors.
Of course. But it's NOT up to you to restrict the user from using whatever techniques xe wants
in existing code. Of course that won't work. But for *new* datatypes choosing one rather than the other gives no difference in terms of denotational semantics.
If it makes the difference for the old code, then it would make the difference for the new code as well.
2) It might be generated by the Template Haskell — which is free to use whatever constructor is fed into it.
OK, so show me what goes wrong!
I hate it. OK, here we go: {-# LANGUAGE TemplateHaskell #-} module TH where import Language.Haskell.TH check :: Name -> ExpQ check c = [|let x = case x of $(conP c [[p|_|]]) -> $(conE c) 1 in x|] {-# LANGUAGE TemplateHaskell #-} module Use where import TH newtype A = A Int deriving Show data B = B !Int deriving Show a = $(check 'A) b = $(check 'B) Prelude> :load "Use.hs" [1 of 2] Compiling TH ( TH.hs, interpreted ) [2 of 2] Compiling Use ( Use.hs, interpreted ) Use.hs:6:1: Warning: Top-level binding with no type signature: a :: A Use.hs:7:1: Warning: Top-level binding with no type signature: b :: B Ok, modules loaded: TH, Use. *Use> a A 1 *Use> b C-c C-cInterrupted.

On Sun, Aug 09, 2015 at 12:16:27AM +0200, MigMit wrote:
1) This might be the code written by someone using your library/framework. In which case it would know about A and B.
Then it's up to me to define and document whatever strictness properties I want for my constructors.
Of course. But it's NOT up to you to restrict the user from using whatever techniques xe wants
Can you explain how any of this "restricts the user from using whatever techniques he wants"?
in existing code. Of course that won't work. But for *new* datatypes choosing one rather than the other gives no difference in terms of denotational semantics.
If it makes the difference for the old code, then it would make the difference for the new code as well.
I fail to see how. Breaking an API makes a difference for old code, but new code is written to the new API.
2) It might be generated by the Template Haskell — which is free to use whatever constructor is fed into it.
OK, so show me what goes wrong!
{-# LANGUAGE TemplateHaskell #-} module TH where import Language.Haskell.TH check :: Name -> ExpQ check c = [|let x = case x of $(conP c [[p|_|]]) -> $(conE c) 1 in x|]
Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish * data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions) from each other. My claim is that * data B = B !A is as indistinguishable from the above four as they are from each other. Tom

On 09 Aug 2015, at 10:39, Tom Ellis
wrote: On Sun, Aug 09, 2015 at 12:16:27AM +0200, MigMit wrote:
1) This might be the code written by someone using your library/framework. In which case it would know about A and B.
Then it's up to me to define and document whatever strictness properties I want for my constructors.
Of course. But it's NOT up to you to restrict the user from using whatever techniques xe wants
Can you explain how any of this "restricts the user from using whatever techniques he wants"?
User can write the code that distinguishes between these two options. You can't force xer not to. You can't even claim that it's a "hack" and shouldn't be allowed.
in existing code. Of course that won't work. But for *new* datatypes choosing one rather than the other gives no difference in terms of denotational semantics.
If it makes the difference for the old code, then it would make the difference for the new code as well.
I fail to see how. Breaking an API makes a difference for old code, but new code is written to the new API.
Code is working or not working regardless of when it was written.
2) It might be generated by the Template Haskell — which is free to use whatever constructor is fed into it.
OK, so show me what goes wrong!
{-# LANGUAGE TemplateHaskell #-} module TH where import Language.Haskell.TH check :: Name -> ExpQ check c = [|let x = case x of $(conP c [[p|_|]]) -> $(conE c) 1 in x|]
Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?

On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote:
Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes. To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim): `data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`. Do you agree or disagree with this statement? Then we may proceed. Tom

I disagree. Отправлено с iPad
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote:
Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.
Tom _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote:
I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution f :: D -> N f (D t) = N t g :: N -> D g (N t) = D t If you disagree that `f . g = id` and `g . f = id` then you must be able to find * a type `T` and either * `n :: N` such that `f (g n)` does not denote the same thing as `n` or * `d :: D` such that `g (f d)` does not denote the same thing as `d` Can you? Tom
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.

First, the half that I agree with: f . g = id. No doubt. But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D). Отправлено с iPad
9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On the contrary, it *is* the same thing Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote:
First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: Right, you can distinguish data declarations from newtype declarations this way, but by using Template Haskell you can also distinguish
* data A = A Int * data A = A { a :: Int } * data A = A' Int * data A = A Int !(), and * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
from each other. My claim is that
* data B = B !A
is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.

You know, you've kinda conviced me. The difference between strict and non-strict parameters is in how constructors work. "data D = D Int" is still the same as "data D = D !Int", but it's constructor — as a function — is more restricted. It's somewhat like defining "d n = D $! n", and then not exporting D, but only d. That said, it might be true that semantics differ depending on what is exported. So, it might be true that your D has the same semantics as N. We still can distinguish between those using various unsafe* hacks — but those are what they are: hacks. Отправлено с iPad
9 авг. 2015 г., в 13:35, Tom Ellis
написал(а): On the contrary, it *is* the same thing
Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined
On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: > Right, you can distinguish data declarations from newtype declarations this > way, but by using Template Haskell you can also distinguish > > * data A = A Int > * data A = A { a :: Int } > * data A = A' Int > * data A = A Int !(), and > * newtype B = B A (where A has one of the above definitions)
Sure, because they are different.
> from each other. My claim is that > > * data B = B !A > > is as indistinguishable from the above four as they are from each other.
Can you please NOT say that some thing can be distinguished AND that they are indistinguishable in the same post?
I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sun, Aug 09, 2015 at 07:49:10PM +0200, MigMit wrote:
You know, you've kinda conviced me.
I hope I'm correct then!
The difference between strict and non-strict parameters is in how constructors work. "data D = D Int" is still the same as "data D = D !Int", but it's constructor — as a function — is more restricted. It's somewhat like defining "d n = D $! n", and then not exporting D, but only d.
Right.
That said, it might be true that semantics differ depending on what is exported. So, it might be true that your D has the same semantics as N. We still can distinguish between those using various unsafe* hacks — but those are what they are: hacks.
Отправлено с iPad
9 авг. 2015 г., в 13:35, Tom Ellis
написал(а): On the contrary, it *is* the same thing
Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined
On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: >> Right, you can distinguish data declarations from newtype declarations this >> way, but by using Template Haskell you can also distinguish >> >> * data A = A Int >> * data A = A { a :: Int } >> * data A = A' Int >> * data A = A Int !(), and >> * newtype B = B A (where A has one of the above definitions) > > Sure, because they are different. > >> from each other. My claim is that >> >> * data B = B !A >> >> is as indistinguishable from the above four as they are from each other. > > Can you please NOT say that some thing can be distinguished AND that they > are indistinguishable in the same post? I think we are perhaps talking at cross purposes.
To clarify, here is an explicit statement (somewhat weaker than the full generality of my claim):
`data D = D !T` and `newtype N = N T` are isomorphic in the sense that there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and `g . f = id`.
Do you agree or disagree with this statement? Then we may proceed.
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

There is still a difference between a data type with one strict field and a newtype: You can strip the constructor of a newtype without evaluating anything. Suppose we have data D = D !D data N = N N d :: D d = D d n :: N n = N n d and n both evaluate to bottom, but it is possible to pattern match on (N t) and succeed, while matching on (D t) does not. Example: mkDs :: Int -> D -> String mkDs i (D t) | i <= 0 = [] | otherwise = 'd' : mkDs (i - 1) t mkNs :: Int -> N -> String mkNs i (N t) | i <= 0 = [] | otherwise = 'n' : mkNs (i - 1) t Evaluating mkNs 5 n returns "nnnnn", while evaluating mkDs 5 d loops forever. Now we can define: f :: D -> N f (D t) = N (f t) g :: N -> D g (N t) = D (g t) id1 :: D -> D id1 = g . f id2 :: N -> N id2 = f . g If both representations should be equal, we should get that mkNs 5 n == mkNs 5 (id2 n). But id2 converts everything to the D type first, which is only inhabited by _|_, and then pattern matches on it. So we get "nnnnn" == _|_, which is obviously false. If we change f to use a lazy pattern match, the equality holds again. So D and N are maybe equivalent if we allow only lazy pattern matches on D. As this is not the case, the two are clearly not equivalent. On 08/09/2015 11:10 PM, Tom Ellis wrote:
On Sun, Aug 09, 2015 at 07:49:10PM +0200, MigMit wrote:
You know, you've kinda conviced me.
I hope I'm correct then!
The difference between strict and non-strict parameters is in how constructors work. "data D = D Int" is still the same as "data D = D !Int", but it's constructor — as a function — is more restricted. It's somewhat like defining "d n = D $! n", and then not exporting D, but only d.
Right.
That said, it might be true that semantics differ depending on what is exported. So, it might be true that your D has the same semantics as N. We still can distinguish between those using various unsafe* hacks — but those are what they are: hacks.
Отправлено с iPad
9 авг. 2015 г., в 13:35, Tom Ellis
написал(а): On the contrary, it *is* the same thing
Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined
On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: I disagree.
Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
> 9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): > On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: >>> Right, you can distinguish data declarations from newtype declarations this >>> way, but by using Template Haskell you can also distinguish >>> >>> * data A = A Int >>> * data A = A { a :: Int } >>> * data A = A' Int >>> * data A = A Int !(), and >>> * newtype B = B A (where A has one of the above definitions) >> >> Sure, because they are different. >> >>> from each other. My claim is that >>> >>> * data B = B !A >>> >>> is as indistinguishable from the above four as they are from each other. >> >> Can you please NOT say that some thing can be distinguished AND that they >> are indistinguishable in the same post? > > I think we are perhaps talking at cross purposes. > > To clarify, here is an explicit statement (somewhat weaker than the full > generality of my claim): > > `data D = D !T` and `newtype N = N T` are isomorphic in the sense that > there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and > `g . f = id`. > > Do you agree or disagree with this statement? Then we may proceed.
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
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

El Aug 10, 2015, a las 7:38, Jonas Scholl
There is still a difference between a data type with one strict field and a newtype: You can strip the constructor of a newtype without evaluating anything.
Suppose we have
data D = D !D
data N = N N
This should be "newtype N = N N", no? Tom
d :: D d = D d
n :: N n = N n
d and n both evaluate to bottom, but it is possible to pattern match on (N t) and succeed, while matching on (D t) does not. Example:
mkDs :: Int -> D -> String mkDs i (D t) | i <= 0 = [] | otherwise = 'd' : mkDs (i - 1) t
mkNs :: Int -> N -> String mkNs i (N t) | i <= 0 = [] | otherwise = 'n' : mkNs (i - 1) t
Evaluating mkNs 5 n returns "nnnnn", while evaluating mkDs 5 d loops forever. Now we can define:
f :: D -> N f (D t) = N (f t)
g :: N -> D g (N t) = D (g t)
id1 :: D -> D id1 = g . f
id2 :: N -> N id2 = f . g
If both representations should be equal, we should get that mkNs 5 n == mkNs 5 (id2 n). But id2 converts everything to the D type first, which is only inhabited by _|_, and then pattern matches on it. So we get "nnnnn" == _|_, which is obviously false. If we change f to use a lazy pattern match, the equality holds again. So D and N are maybe equivalent if we allow only lazy pattern matches on D. As this is not the case, the two are clearly not equivalent.
On 08/09/2015 11:10 PM, Tom Ellis wrote:
On Sun, Aug 09, 2015 at 07:49:10PM +0200, MigMit wrote:
You know, you've kinda conviced me.
I hope I'm correct then!
The difference between strict and non-strict parameters is in how constructors work. "data D = D Int" is still the same as "data D = D !Int", but it's constructor — as a function — is more restricted. It's somewhat like defining "d n = D $! n", and then not exporting D, but only d.
Right.
That said, it might be true that semantics differ depending on what is exported. So, it might be true that your D has the same semantics as N. We still can distinguish between those using various unsafe* hacks — but those are what they are: hacks.
Отправлено с iPad
9 авг. 2015 г., в 13:35, Tom Ellis
написал(а): On the contrary, it *is* the same thing
Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined
On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
> 9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): > > On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: > I disagree. Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
>> 9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): >> On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: >>>> Right, you can distinguish data declarations from newtype declarations this >>>> way, but by using Template Haskell you can also distinguish >>>> >>>> * data A = A Int >>>> * data A = A { a :: Int } >>>> * data A = A' Int >>>> * data A = A Int !(), and >>>> * newtype B = B A (where A has one of the above definitions) >>> >>> Sure, because they are different. >>> >>>> from each other. My claim is that >>>> >>>> * data B = B !A >>>> >>>> is as indistinguishable from the above four as they are from each other. >>> >>> Can you please NOT say that some thing can be distinguished AND that they >>> are indistinguishable in the same post? >> >> I think we are perhaps talking at cross purposes. >> >> To clarify, here is an explicit statement (somewhat weaker than the full >> generality of my claim): >> >> `data D = D !T` and `newtype N = N T` are isomorphic in the sense that >> there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and >> `g . f = id`. >> >> Do you agree or disagree with this statement? Then we may proceed.
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
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

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Of course... my mistake. And now we also have mkNs 5 n == mkNs 5 (id2 n), without any lazy pattern matching. But we still have mkDs 5 d == _|_, so N and D still have different semantics. On 08/10/2015 03:56 PM, amindfv@gmail.com wrote: > El Aug 10, 2015, a las 7:38, Jonas Schollescribió: > >> There is still a difference between a data type with one strict field >> and a newtype: You can strip the constructor of a newtype without >> evaluating anything. >> >> Suppose we have >> >> data D = D !D >> >> data N = N N >> > > This should be "newtype N = N N", no? > > Tom > >> d :: D >> d = D d >> >> n :: N >> n = N n >> >> d and n both evaluate to bottom, but it is possible to pattern match on >> (N t) and succeed, while matching on (D t) does not. Example: >> >> mkDs :: Int -> D -> String >> mkDs i (D t) | i <= 0 = [] >> | otherwise = 'd' : mkDs (i - 1) t >> >> mkNs :: Int -> N -> String >> mkNs i (N t) | i <= 0 = [] >> | otherwise = 'n' : mkNs (i - 1) t >> >> Evaluating mkNs 5 n returns "nnnnn", while evaluating mkDs 5 d loops >> forever. Now we can define: >> >> f :: D -> N >> f (D t) = N (f t) >> >> g :: N -> D >> g (N t) = D (g t) >> >> id1 :: D -> D >> id1 = g . f >> >> id2 :: N -> N >> id2 = f . g >> >> If both representations should be equal, we should get that mkNs 5 n == >> mkNs 5 (id2 n). But id2 converts everything to the D type first, which >> is only inhabited by _|_, and then pattern matches on it. So we get >> "nnnnn" == _|_, which is obviously false. If we change f to use a lazy >> pattern match, the equality holds again. So D and N are maybe equivalent >> if we allow only lazy pattern matches on D. As this is not the case, the >> two are clearly not equivalent. >> >> On 08/09/2015 11:10 PM, Tom Ellis wrote: >>> On Sun, Aug 09, 2015 at 07:49:10PM +0200, MigMit wrote: >>>> You know, you've kinda conviced me. >>> >>> I hope I'm correct then! >>> >>>> The difference between strict and non-strict parameters is in how >>>> constructors work. "data D = D Int" is still the same as "data D = D >>>> !Int", but it's constructor — as a function — is more restricted. It's >>>> somewhat like defining "d n = D $! n", and then not exporting D, but only >>>> d. >>> >>> Right. >>> >>>> That said, it might be true that semantics differ depending on what is >>>> exported. So, it might be true that your D has the same semantics as N. >>>> We still can distinguish between those using various unsafe* hacks — but >>>> those are what they are: hacks. >>>> >>>> Отправлено с iPad >>>> >>>>> 9 авг. 2015 г., в 13:35, Tom Ellis написал(а): >>>>> >>>>> On the contrary, it *is* the same thing >>>>> >>>>> Prelude> data D = D !Int deriving Show >>>>> Prelude> D undefined >>>>> *** Exception: Prelude.undefined >>>>> Prelude> undefined :: D >>>>> *** Exception: Prelude.undefined >>>>> >>>>> >>>>>> On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: >>>>>> First, the half that I agree with: f . g = id. No doubt. >>>>>> >>>>>> But g . f > id. And the value "d" that you want is "undefined". g (f >>>>>> undefined) = D undefined, which is not the same as (undefined :: D). >>>>>> >>>>>> Отправлено с iPad >>>>>> >>>>>>>> 9 авг. 2015 г., в 13:17, Tom Ellis написал(а): >>>>>>>> >>>>>>>> On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: >>>>>>>> I disagree. >>>>>>> >>>>>>> Ah, good. A concrete point of disagreement. What, then, is wrong with the >>>>>>> solution >>>>>>> >>>>>>> f :: D -> N >>>>>>> f (D t) = N t >>>>>>> >>>>>>> g :: N -> D >>>>>>> g (N t) = D t >>>>>>> >>>>>>> If you disagree that `f . g = id` and `g . f = id` then you must be able to >>>>>>> find >>>>>>> >>>>>>> * a type `T` >>>>>>> >>>>>>> and either >>>>>>> >>>>>>> * `n :: N` such that `f (g n)` does not denote the same thing as `n` >>>>>>> >>>>>>> or >>>>>>> >>>>>>> * `d :: D` such that `g (f d)` does not denote the same thing as `d` >>>>>>> >>>>>>> Can you? >>>>>>> >>>>>>> Tom >>>>>>> >>>>>>> >>>>>>>>> 9 авг. 2015 г., в 12:37, Tom Ellis написал(а): >>>>>>>>> On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: >>>>>>>>>>> Right, you can distinguish data declarations from newtype declarations this >>>>>>>>>>> way, but by using Template Haskell you can also distinguish >>>>>>>>>>> >>>>>>>>>>> * data A = A Int >>>>>>>>>>> * data A = A { a :: Int } >>>>>>>>>>> * data A = A' Int >>>>>>>>>>> * data A = A Int !(), and >>>>>>>>>>> * newtype B = B A (where A has one of the above definitions) >>>>>>>>>> >>>>>>>>>> Sure, because they are different. >>>>>>>>>> >>>>>>>>>>> from each other. My claim is that >>>>>>>>>>> >>>>>>>>>>> * data B = B !A >>>>>>>>>>> >>>>>>>>>>> is as indistinguishable from the above four as they are from each other. >>>>>>>>>> >>>>>>>>>> Can you please NOT say that some thing can be distinguished AND that they >>>>>>>>>> are indistinguishable in the same post? >>>>>>>>> >>>>>>>>> I think we are perhaps talking at cross purposes. >>>>>>>>> >>>>>>>>> To clarify, here is an explicit statement (somewhat weaker than the full >>>>>>>>> generality of my claim): >>>>>>>>> >>>>>>>>> `data D = D !T` and `newtype N = N T` are isomorphic in the sense that >>>>>>>>> there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and >>>>>>>>> `g . f = id`. >>>>>>>>> >>>>>>>>> Do you agree or disagree with this statement? Then we may proceed. >>>>> _______________________________________________ >>>>> 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 >>> _______________________________________________ >>> 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBCAAGBQJVyOUvAAoJEM0PYZBmfhoBXnIIALpMHC8BHMN5EgKREFQfsX6k dhyhOxaxDxHM7gYcgxKo5il2rtDJT5sNjuzeyBfBpxKqmjg1YSLEsB3E2fGC8JZV LOBCROAqbcmAXRPNAKdbMaT8XYaLrMqFkqWpcNjxe752RAbqgbb9II1O7GLKYSDZ Q3cEIvBRBHcfeQKQRHnbunXdrdojRZf9LdSbm/HkzatPdToAMUsTdhNAPRhnxM5R EHP2uhmtwRzEcCZ+/tzeJ/OYltqZ9hJ419CwwJs8z9hJUprLpdK4HS76IvDde3kq wFUWqzpESAgOxrtp6lTwLFSsOsXyg3PM4x0+F27sA/z+ls6GrvYM+VcXa4XMBlU= =jkjF -----END PGP SIGNATURE-----

I'm not claiming that they should behave the same with regard to the *syntax* of case analysis (NB case analysis of newtypes is already treated specially since the constructor doesn't actually exist). Again I refer interested parties to my proposed translation between the two methods of defining types: http://stackoverflow.com/posts/21331284/revisions Does this answer your objection? Tom On Mon, Aug 10, 2015 at 01:38:23PM +0200, Jonas Scholl wrote:
There is still a difference between a data type with one strict field and a newtype: You can strip the constructor of a newtype without evaluating anything.
Suppose we have
data D = D !D
data N = N N
d :: D d = D d
n :: N n = N n
d and n both evaluate to bottom, but it is possible to pattern match on (N t) and succeed, while matching on (D t) does not. Example:
mkDs :: Int -> D -> String mkDs i (D t) | i <= 0 = [] | otherwise = 'd' : mkDs (i - 1) t
mkNs :: Int -> N -> String mkNs i (N t) | i <= 0 = [] | otherwise = 'n' : mkNs (i - 1) t
Evaluating mkNs 5 n returns "nnnnn", while evaluating mkDs 5 d loops forever. Now we can define:
f :: D -> N f (D t) = N (f t)
g :: N -> D g (N t) = D (g t)
id1 :: D -> D id1 = g . f
id2 :: N -> N id2 = f . g
If both representations should be equal, we should get that mkNs 5 n == mkNs 5 (id2 n). But id2 converts everything to the D type first, which is only inhabited by _|_, and then pattern matches on it. So we get "nnnnn" == _|_, which is obviously false. If we change f to use a lazy pattern match, the equality holds again. So D and N are maybe equivalent if we allow only lazy pattern matches on D. As this is not the case, the two are clearly not equivalent.
On 08/09/2015 11:10 PM, Tom Ellis wrote:
On Sun, Aug 09, 2015 at 07:49:10PM +0200, MigMit wrote:
You know, you've kinda conviced me.
I hope I'm correct then!
The difference between strict and non-strict parameters is in how constructors work. "data D = D Int" is still the same as "data D = D !Int", but it's constructor — as a function — is more restricted. It's somewhat like defining "d n = D $! n", and then not exporting D, but only d.
Right.
That said, it might be true that semantics differ depending on what is exported. So, it might be true that your D has the same semantics as N. We still can distinguish between those using various unsafe* hacks — but those are what they are: hacks.
Отправлено с iPad
9 авг. 2015 г., в 13:35, Tom Ellis
написал(а): On the contrary, it *is* the same thing
Prelude> data D = D !Int deriving Show Prelude> D undefined *** Exception: Prelude.undefined Prelude> undefined :: D *** Exception: Prelude.undefined
On Sun, Aug 09, 2015 at 01:30:01PM +0200, MigMit wrote: First, the half that I agree with: f . g = id. No doubt.
But g . f > id. And the value "d" that you want is "undefined". g (f undefined) = D undefined, which is not the same as (undefined :: D).
Отправлено с iPad
> 9 авг. 2015 г., в 13:17, Tom Ellis
написал(а): > > On Sun, Aug 09, 2015 at 01:09:21PM +0200, MigMit wrote: > I disagree. Ah, good. A concrete point of disagreement. What, then, is wrong with the solution
f :: D -> N f (D t) = N t
g :: N -> D g (N t) = D t
If you disagree that `f . g = id` and `g . f = id` then you must be able to find
* a type `T`
and either
* `n :: N` such that `f (g n)` does not denote the same thing as `n`
or
* `d :: D` such that `g (f d)` does not denote the same thing as `d`
Can you?
Tom
>> 9 авг. 2015 г., в 12:37, Tom Ellis
написал(а): >> On Sun, Aug 09, 2015 at 12:15:47PM +0200, MigMit wrote: >>>> Right, you can distinguish data declarations from newtype declarations this >>>> way, but by using Template Haskell you can also distinguish >>>> >>>> * data A = A Int >>>> * data A = A { a :: Int } >>>> * data A = A' Int >>>> * data A = A Int !(), and >>>> * newtype B = B A (where A has one of the above definitions) >>> >>> Sure, because they are different. >>> >>>> from each other. My claim is that >>>> >>>> * data B = B !A >>>> >>>> is as indistinguishable from the above four as they are from each other. >>> >>> Can you please NOT say that some thing can be distinguished AND that they >>> are indistinguishable in the same post? >> >> I think we are perhaps talking at cross purposes. >> >> To clarify, here is an explicit statement (somewhat weaker than the full >> generality of my claim): >> >> `data D = D !T` and `newtype N = N T` are isomorphic in the sense that >> there exist `f :: D -> N` and `g :: N -> D` such that `f . g = id` and >> `g . f = id`. >> >> Do you agree or disagree with this statement? Then we may proceed.
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
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

First of all, I have no opinion of my own -- I merely share the memories
of also being confused by "type" not introducing a true type, but a type macro.
However there's something in the argument that I don't think is right..
Sven Panne
+1 to this. As has already been noted in a different thread, the "let's introduce a pragma, nobody is forced to use it" argument is bogus. It fragments the language for no good reason
One moment, please, the good reason has been provided -- newbie confusion.
and one *is* forced to handle it (when reading other people's code etc.). Furthermore, the current example at hand is roughly at position 2 of Wadler's Law of Language Design (https://wiki.haskell.org/Wadler's_Law), a.k.a. bikeshedding at the lexical level. ;-)
I don't think that the concept of "bikeshedding", on its own, should be used as a tool to prevent constructive discussion of material shortcomings.
Don't get me wrong: If Haskell was still at its infancy, renaming things would probably be beneficial, as "type" is somehow really a misnomer. One could discuss if "newtype" is really needed when we have "data" plus strictness annotations (personally I'm a bit unsure what the common use case for "case" is where they differ and what one might consider more natural). Given the case that people seem to move towards GADTs anyway, the discussion seems a bit moot.
The context here is newbies -- so existence of GADTs (which are an advanced concept, let's not forget that!) is off-topic to the proposal.
Just my 2c.
P.S.: If you think this part of Haskell is a bit hard to read and confusing, I seriously propose a week or two in C++'s wonderful meta-programming world with templates... ;-)
Hey, we're talking about language adoption here.. I presume.. ..and if we do, any undeserved complication does hurt. So, in the end, the idea will probably be shot down -- but please, let's shoot it down for a good reason, with a clear, irrefutable understanding of why. -- respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

One moment, please, the good reason has been provided -- newbie confusion.
I'm really happy that we are at the stage when this is considered an important source of newbie confusion in Haskell.
So, in the end, the idea will probably be shot down -- but please, let's shoot it down for a good reason, with a clear, irrefutable understanding of why.
The reason is very simple, and it was stated several times already: it will break everything that was written so far, and there is not enough evidence that things would be even a little better. In fact, moving to dynamic typing would a) break less existing code (could be none, if done carefully), and b) remove A LOT of newbie confusion, but for some reason I don't think it's a good idea either.

On Sat, Aug 8, 2015 at 4:59 PM, MigMit
So, in the end, the idea will probably be shot down -- but please, let's shoot it down for a good reason, with a clear, irrefutable understanding of why.
The reason is very simple, and it was stated several times already: it will break everything that was written so far, and there is not enough evidence that things would be even a little better.
This is quite important, folks. Don't tell us how tools will mitigate this. Go look at Python 3's adoption rate --- and it does have the tools --- and tell me again how well that path works for an established language. (Hint: every Python package I use has no intention of moving to Python 3.) Haskell may be new to you personally. That does not mean that it's okay to break what, more than 15 years worth of code? -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Brandon Allbery
Haskell may be new to you personally. That does not mean that it's okay to break what, more than 15 years worth of code?
I don't mean to come across argumentative -- rather I merely want to point out, that the way of accomplishing it in a non-breaking way was proposed in this thread: an extra keyword + a pragma to guard against name clashes. Whether it's worth the trouble or not -- again, I have no opinion of my own. Please, let's discuss the idea on its merit, and not criticize some flawed version of it. -- respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

On Sat, Aug 8, 2015 at 5:21 PM, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
I don't mean to come across argumentative -- rather I merely want to point out, that the way of accomplishing it in a non-breaking way was proposed in this thread: an extra keyword + a pragma to guard against name clashes.
Not worth the fact that I now have to look for two effectively incompatible languages both calling themselves Haskell. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Brandon Allbery
On Sat, Aug 8, 2015 at 5:21 PM, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
I don't mean to come across argumentative -- rather I merely want to point out, that the way of accomplishing it in a non-breaking way was proposed in this thread: an extra keyword + a pragma to guard against name clashes.
Not worth the fact that I now have to look for two effectively incompatible languages both calling themselves Haskell.
Do you mean that adding a new keyword to the language, guarded by a language extension, really deserves this strong wording -- "effectively incompatible"? What about the precedents? - TransformListComp: group, by, using - RecursiveDo: mdo, rec Then, I guess there was a number of keywords introduced by Haskell 2010. -- respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

On Sat, Aug 8, 2015 at 5:33 PM, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
Do you mean that adding a new keyword to the language, guarded by a language extension, really deserves this strong wording -- "effectively incompatible"?
Do you mean that I am required to look for this in every program so I know what *actual* language this program is written in? Do you mean that people who have years oof experience with a lnaguage should fully expect to have to look for new magic that means this program is not actually written in the language they know? Do you mean that stability is completely meaningless when you can pretend that adding a pragma justifies changing anything you feel like? Or do you mean that stability simply is not a thing at all? -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sat, Aug 8, 2015 at 5:33 PM, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
What about the precedents?
- TransformListComp: group, by, using - RecursiveDo: mdo, rec
If I see those then I know that something is going on. If I don't see `data` and do see some new keyword in its place then I am not reading Haskell code; I know this because I have Haskell code and it uses `data`. If I see `data` and it means something different than what Haskell means by `data`? Oh right, you have decided your incompatible language is still "Haskell", I just have to deal with the fact that it means something completely different now. Have you ever come back to a project 6 months after you last worked on it, or a year after, and had to figure it out again? Consider how well that works when you've decided to mutate the language in the meantime, and your mission critical code was not permitted to be rewritten in the new language because it is mission critical and "oh just run this automated script to fix it" is not a plan that passes business review processes. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On 8 August 2015 at 14:03, Brandon Allbery
This is quite important, folks. Don't tell us how tools will mitigate this. Go look at Python 3's adoption rate --- and it does have the tools --- and tell me again how well that path works for an established language. (Hint: every Python package I use has no intention of moving to Python 3.)
That seems like a red herring to me. Python does not have a type system to speak of so even simple refactoring is painful. Let alone making changes to the language itself...
Haskell may be new to you personally. That does not mean that it's okay to break what, more than 15 years worth of code?
So when is it okay? :-) Still, if proper tooling automates it, why not? (I'm not advocating making language changes willy-nilly but the argument "it breaks existing code" [in and of itself] implies we are stuck with all mistakes made in the past. Language designers are human too. We need a way forward.)

It's generally considered OK to break small parts of code. For example, the infamous n+k patterns — if I remember correctly, they are still here, but you have to enable them explicitly. It breaks SOME code, but not a lot of it. We might even break a lot of code, but we surely need a VERY good reason to do that.
On 08 Aug 2015, at 23:27, Hilco Wijbenga
wrote: On 8 August 2015 at 14:03, Brandon Allbery
wrote: This is quite important, folks. Don't tell us how tools will mitigate this. Go look at Python 3's adoption rate --- and it does have the tools --- and tell me again how well that path works for an established language. (Hint: every Python package I use has no intention of moving to Python 3.)
That seems like a red herring to me. Python does not have a type system to speak of so even simple refactoring is painful. Let alone making changes to the language itself...
Haskell may be new to you personally. That does not mean that it's okay to break what, more than 15 years worth of code?
So when is it okay? :-) Still, if proper tooling automates it, why not?
(I'm not advocating making language changes willy-nilly but the argument "it breaks existing code" [in and of itself] implies we are stuck with all mistakes made in the past. Language designers are human too. We need a way forward.)

On Sat, Aug 8, 2015 at 5:27 PM, Hilco Wijbenga
So when is it okay? :-) Still, if proper tooling automates it, why not?
Because your tooling does not rewire people's brains and does not automatically run itself on other people's programs when you *look* at them. Because not everyone builds houses of cards and leaves them to collapse on their successor while they go chase the next cool thing, like certain modern "web programmers" who seems to have confused "agile" with "fragile" and therefore think Go rewriting its syntax every week is somehow sensible. And not everyone *can* do things the zero-memory-change-it-all-who-cares-it'll-all-be-different-next-week way; that does not fly on the business end, for example. (I've had people claim to me that they do that with accounting packages. I bet they've never faced an audit and will be learning the hard way why business does not work that way when the auditors *do* show up.) -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On 8 August 2015 at 14:46, Brandon Allbery
On Sat, Aug 8, 2015 at 5:27 PM, Hilco Wijbenga
wrote: So when is it okay? :-) Still, if proper tooling automates it, why not?
Because your tooling does not rewire people's brains and does not automatically run itself on other people's programs when you *look* at them.
I don't think we need to "rewire" our brains to accommodate a few language tweaks. The haskell-fix tool I had in mind would change the source code so you would look at the fixed code. But, yes, there would be a transition time. In my experience that is true for any change to your code base.
Because not everyone builds houses of cards and leaves them to collapse on their successor while they go chase the next cool thing, like certain modern "web programmers" who seems to have confused "agile" with "fragile" and therefore think Go rewriting its syntax every week is somehow sensible. And not everyone *can* do things the zero-memory-change-it-all-who-cares-it'll-all-be-different-next-week way; that does not fly on the business end, for example. (I've had people claim to me that they do that with accounting packages. I bet they've never faced an audit and will be learning the hard way why business does not work that way when the auditors *do* show up.)
You seem to be creating a whole mountain range out of a mole hill. :-) I have to maintain a very low quality, legacy code base. So I totally sympathize with the "house of cards" and "fragile" part of your paragraph. But after that I have a hard time making sense of it. Nobody is advocating changing Haskell to look like a completely different language. Or that we start making language changes every week. I really do not understand where you got that impression. Everybody is talking about tiny language tweaks that (hopefully) make the language better. If the agreement is that, yes, it does make the language better than the blanket counter argument "it breaks existing code" should not stop progress. If you don't make improvements now because of existing code then tomorrow there will be more existing code and thus even more inertia.

El Aug 8, 2015, a las 19:01, Hilco Wijbenga
On 8 August 2015 at 14:46, Brandon Allbery
wrote: On Sat, Aug 8, 2015 at 5:27 PM, Hilco Wijbenga
wrote: So when is it okay? :-) Still, if proper tooling automates it, why not?
Because your tooling does not rewire people's brains and does not automatically run itself on other people's programs when you *look* at them.
I don't think we need to "rewire" our brains to accommodate a few language tweaks. The haskell-fix tool I had in mind would change the source code so you would look at the fixed code. But, yes, there would be a transition time. In my experience that is true for any change to your code base.
An analogy: Haskell's use of "::" and ":" are backwards. Most ML-style languages use ":" for types but iiuc it was assumed using cons would be so common that it should be the quicker one to type. Obviously that estimation was wrong -- we write a lot more types than conses. Annoying? Yes. Way too late and fundamental to change? Also yes. I also disagree with the better-ness (even for beginners) of the proposed changes, but point 1 supersedes that. tom
Because not everyone builds houses of cards and leaves them to collapse on their successor while they go chase the next cool thing, like certain modern "web programmers" who seems to have confused "agile" with "fragile" and therefore think Go rewriting its syntax every week is somehow sensible. And not everyone *can* do things the zero-memory-change-it-all-who-cares-it'll-all-be-different-next-week way; that does not fly on the business end, for example. (I've had people claim to me that they do that with accounting packages. I bet they've never faced an audit and will be learning the hard way why business does not work that way when the auditors *do* show up.)
You seem to be creating a whole mountain range out of a mole hill. :-)
I have to maintain a very low quality, legacy code base. So I totally sympathize with the "house of cards" and "fragile" part of your paragraph. But after that I have a hard time making sense of it.
Nobody is advocating changing Haskell to look like a completely different language. Or that we start making language changes every week. I really do not understand where you got that impression. Everybody is talking about tiny language tweaks that (hopefully) make the language better. If the agreement is that, yes, it does make the language better than the blanket counter argument "it breaks existing code" should not stop progress. If you don't make improvements now because of existing code then tomorrow there will be more existing code and thus even more inertia. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

2015-08-09 1:01 GMT+02:00 Hilco Wijbenga
On 8 August 2015 at 14:46, Brandon Allbery
wrote: [...] Everybody is talking about tiny language tweaks that (hopefully) make the language better. If the agreement is that, yes, it does make the language better than the blanket counter argument "it breaks existing code" should not stop progress. If you don't make improvements now because of existing code then tomorrow there will be more existing code and thus even more inertia.
This is flawed reasoning, ignoring basically all reality in business, larger projects, legacy projects etc.: Of course languages should evolve, but there is always a cost associated with it, and this should outweigh the disadvantages. And I can't see this happening here at all: We are talking about perhaps 5min of confusion (if at all) when starting Haskell compared to millions (billions?) of existing LOC perhaps needing a change (or not, who knows?), books (which can't be updated by something like 'gofix'), brains of people using Haskell for over a decade etc. Programming languages are just like natural languages: Even if they are often irregular, they form a common ground for communication and understanding each other. Do we need irregular verbs? No. But try to take them away from native speakers... :-) Are irregular verbs really a problem? No. When you are fluent in a language you don't even think about them anymore.

Obviously a change like this would be horribly destructive to existing code, and for relatively little benefit. There are much larger barriers to understanding Haskell than syntactic trivia like the one mentioned here. That said, it seems reasonable to claim that the existing type declaration keywords (type, data, newtype) can be confusing to newbies and somewhat non-intuitive. If we could go back in time and change these small details without breaking swathes of existing code, perhaps we would. I'm sure most of us who use Haskell on a regular basis have a number of small gripes about the language that aren't important enough to break things over, but would still be convenient or aesthetically pleasing to change if we're going to break backwards compatibility anyway. It is worth asking, then, if we should record these small aesthetic suggestions somewhere for consideration while designing the next major compatibility-breaking release of the Haskell specification (Haskell 2020 or what have you). -Will

Will Yager
Obviously a change like this would be horribly destructive to existing code, and for relatively little benefit.
Hidden behind a pragma -- is it really that horribly destructive?
There are much larger barriers to understanding Haskell than syntactic trivia like the one mentioned here.
Barriers exist at every level of mastery -- the ones that matter are those present at the exact point of one's progression. It is, of course, true that at some point the little awkwardness exits the picture completely -- but for any point of time, there always will be a nontrivial set of people thinking to themselves -- "what the heck?!".
It is worth asking, then, if we should record these small aesthetic suggestions somewhere for consideration while designing the next major compatibility-breaking release of the Haskell specification (Haskell 2020 or what have you).
Now this is exactly the kind of assessment that I have been hoping to hear! And the idea is that, before the next release of the language specification, it is possible to experiment with the keyword hidden behind the pragma -- and should the result prove satisfactory, all that remains to do is to lift the pragma requirement. Maybe a unified playground for such tweaks -- XNewSyntax, for example. Or do people suggest, that it should somehow suddenly materialise in the next release of the specification, without touching any staging ground? -- respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

On Sat, Aug 8, 2015 at 6:30 PM Will Yager
It is worth asking, then, if we should record these small aesthetic suggestions somewhere for consideration while designing the next major compatibility-breaking release of the Haskell specification (Haskell 2020 or what have you).
No, let's not be so optimistic (or pessimistic, depending on how you feel) and call it Haskell 3000 .

GHC ships with a solution to the original problem about a very confusing `=`: GADT syntax. I *don't* mean GADTs, in all their glory. It has been correctly observed that GADTs are not for the beginner. But there's nothing wrong at all with GADT syntax:
data Maybe a where Just :: a -> Maybe a Nothing :: Maybe a
This syntax seems to fix the original poster's problem, and more besides. I've had several beginners complain about things like `data Foo = MkFoo Int`, in that we see the phrase `MkFoo Int`, but these two things -- a data constructor and a type -- belong to two very different parts of the syntax. GADT syntax gets rid of these problems. Perhaps tutorials/teachers should just introduce GADT syntax and mention traditional syntax as a historical note. GHC even has the -XGADTSyntax extension to allow the syntax without full-blown GADTs. To respond to two other strands of this thread: - Beyond the difference already observed between `data` and `newtype`, using `newtype` also works with GHC's Coercible mechanism. This is, of course, GHC-specific and not part of standard Haskell, but I thought it worth mentioning. See https://wiki.haskell.org/GHC/Coercible - Haskellers are, of course, welcome to change keywords in their files via CPP. This works, for example:
#define datatype data #define alias type
datatype Boolean = F | T alias IsGood = Boolean
invert :: IsGood -> IsGood invert T = F invert F = T
You can even specify such things via the command line via the -D option, so you could create a wrapper around GHC that added the necessary -D options and then you'd have your new language, with nothing extra in your source files. Good luck sharing your code though! Note that this solution is functionally equivalent to hiding the change behind a pragma. Richard

On 9/08/2015, at 8:59 am, MigMit
The reason is very simple, and it was stated several times already: it will break everything that was written so far, and there is not enough evidence that things would be even a little better.
Not just everything written IN Haskell, but everything written ABOUT Haskell as well. Like books, lecture notes, tutorials, ... For what it's worth, -- Ada type I1 is new Integer; -- isomorphic but incompatible subtype I2 is Integer; -- just an alias (* ML *) datatype 'a tsil = LIN | SNOC of 'a tsil * 'a type revints = int stil (* spell the words backwards *) // F# type T = A | B | C;; type R = T;; // How's that for confusing? Defining a new type (T) and an // alias (R) use the *same* keyword and operator! type S = S;; // defines a new type with one constructor, but // type S = T;; // would have defined S as an alias for T. % Mercury :- type strange ---> foo(int) ; bar(string). :- type wierd == strange. % different operator. % Erlang -type tsil(T) :: nil | {snoc,tsil(T),T}. -type revints() :: tsil(integer()). % This looks as bad as F#, but it's either better or worse, % depending on your viewpoint. Erlang types are not % Hindley-Milner types and it does not have ADT (sum-of- % products) declarations. 'snoc' above is just a constant. % BOTH declarations are aliases. // Clean ::Tsil a = Lin | Snoc (Tsil a) a ::RevInts :== Tsil Int //different operator "::" is used to *introduce* a type in Clean, presumably because infix "::" is used to *apply* a type (Empty :: RevInts). Haskell is actually pretty clear.

On 8 August 2015 at 12:14, Oliver Charles
I hate to come across as a party-pooper, but do we really think it's realistic that any of these changes will be made? I understand that there may be benefits, but they don't outweigh the cost of breaking almost all existing code. The idea of opting in to this with pragmas just doesn't seem worth it, and further fragments the language, ultimately reducing readability when I pick up another author's code.
I have no doubt that you are right: nothing will change. However... I don't agree that "it breaks existing code" is a valid argument (in and of itself). You can stick with whatever version of the compiler you are currently using. Nobody is forcing you to change *immediately*. Holding a programming language hostage because of the existing code base is irresponsible. Having said that, changing the language "often" is also irresponsible. So it should only happen, say, every 5 years. More importantly, it should be done by means of a tool (a la gofix, see [1]). Especially with a language like Haskell, it should be relatively simple to create a haskell-fix tool that replaces deprecated structure/syntax with its new-and-improved alternative. Make haskell-fix a standard part of the toolchain and most (all?) problems related to language changes disappear. (I would be very interested to know whether the availability of such a haskell-fix tool would change people's opinion about making language changes.) [1] http://blog.golang.org/introducing-gofix

I saw this as an entertaining "If I could wish..." survey, and don't think anybody is thinking seriously to implement it. If you asked me a few years ago, I'd say yes, go ahead. The benefit/cost rate is far too small, if any. As for the 'beginner-friendly' reason for change, while learning Haskell, it took me half an hour of confusion, and another half an hour of anger of how 'stupid' Haskell is. :-) But it is interesting to see how many people responded to this. Obviously a touchy thing.
-------- Original Message -------- Subject: Re: [Haskell-cafe] an idea for modifiyng data/newtype syntax: use `::=` instead of `=` From: Oliver Charles
To: Geraldus , Hilco Wijbenga , Haskell Cafe Date: 08/08/15 21:14 I hate to come across as a party-pooper, but do we really think it's realistic that any of these changes will be made? I understand that there may be benefits, but they don't outweigh the cost of breaking almost all existing code. The idea of opting in to this with pragmas just doesn't seem worth it, and further fragments the language, ultimately reducing readability when I pick up another author's code.
/ocharles/
On Sat, Aug 8, 2015 at 7:29 PM Geraldus
mailto:heraldhoi@gmail.com> wrote: type -> alias data -> data newtype -> newalias or newdata?
`type` keyword was really confusing for me at the beginning, also this confusion brought another one about `newtype` keyword.
сб, 8 авг. 2015 г. в 22:28, Hilco Wijbenga
mailto:hilco.wijbenga@gmail.com>: On 8 August 2015 at 08:03, Daniel Trstenjak
mailto:daniel.trstenjak@gmail.com> wrote: >> type Name = String >> data Date = Date Int Int Int > > if we're at it ;), then please change it to: > > alias Name = String > type Date = Date Int Int Int I wholeheartedly agree with replacing the current "type" with "alias", the current "type" is just flat out wrong: it does *not* create a type. This should be very simple to do too: introduce "alias" as a new keyword and deprecate "type". No existing code would be affected.
I'm on the fence about "type" instead of "data", though. "data" clearly conveys the meaning that we are talking about data only, not functions on that data (as in OOP). (Well, data constructors are functions too.) Then again, as soon as you add "deriving", you *are* defining functions on that data. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org mailto:Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org mailto: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 9/08/2015, at 5:28 am, Hilco Wijbenga
I wholeheartedly agree with replacing the current "type" with "alias",
That would be to replace a precise time with a vague one. All sorts of things can be aliased. Especially when someone writes code that I would like to use, but writes it with an American accent, I would very much like to be able to do things *like* alias Colour = Color and I would like to do this for types, for modules, for constructors, for plain functions, &c. I am not saying that the same *form* of declaration should be used in each case, but that declarations all using the _word_ 'alias' should be available to make it quite plain to the reader what is going on. In particular, there is currently no way to give an additional name to a constructor that lets you *use* it as a constructor. 'alias' is far too potentially useful a word to squander this way. Consider ML: val thingy = 42 This says that 'thingy' is another name for the val(ue) 42. It's an equality for values. type 'a sequence = 'a list This says that for any type 't, 't sequence is another name for the type 't list. It's an equality for types. Type is the *perfect* word here, in ML. Now Haskell does not the 'val' keyword (nor the 'fun' keyword) and it doesn't need them. But it *does* have two kinds of type declaration, one of which *IS* a type equality type Sequence t = [t] and *looks like it*, and one of which ISN'T a type equality. If you want to argue about the grammar of 'data' declarations, go right ahead. I myself was a little confused by them at the beginning, because the declare a new type and some constructors, but do not declare 'data' in the sense that a DATA statement in BASIC or Fortran or the DATA DIVISION in COBOL does. The word 'data' is so very far from conveying the idea "introduce a new type" that it's not funny. But to be confused by 'type' declarations? That's, well, alien. Especially as in the Haskell teaching material I'm familiar with, people are introduced to 'type' declarations doing the utterly familiar 'introduce a new name for an old type' thing before they're introduced to 'data'.
the current "type" is just flat out wrong: it does *not* create a type.
AND IT DOES NOT *CLAIM* TO CREATE A TYPE. It claims to state a type EQUALITY, and it really does. It's just like if you write answer = 42 This does NOT define a new constant. It gives a new name for an existing value. Next thing someone will say that this ought to be written value_alias answer = 42 or something.

Hi Richard,
AND IT DOES NOT *CLAIM* TO CREATE A TYPE. It claims to state a type EQUALITY, and it really does.
I can understand your reasoning and only looking at 'type' it's fine, but you've to look at 'type/newtype/data' at once, and if 'data' is strangely named, what other name would you take for it?
It's just like if you write
answer = 42
This does NOT define a new constant. It gives a new name for an existing value. Next thing someone will say that this ought to be written
value_alias answer = 42
or something.
But with this point of view 'type Sequence t = [t]' is also somehow bogus and should just be 'Sequence t = [t]'. Greetings, Daniel

On 18/08/2015, at 6:49 pm, Daniel Trstenjak
Hi Richard,
AND IT DOES NOT *CLAIM* TO CREATE A TYPE. It claims to state a type EQUALITY, and it really does.
I can understand your reasoning and only looking at 'type' it's fine, but you've to look at 'type/newtype/data' at once, and if 'data' is strangely named, what other name would you take for it?
Oh, I _have_ looked at them all at once. In fact looking at them all at once was precisely what made it easy to understand them. newtype *says* it introduces a new type, and it *does*. The distinction between type and newtype makes it extremely hard to be confused about 'type', or any rate hard to both be awake and *remain* confused for long. What name would I have used for 'data'? While 'newtype T x = T x' and 'data T x = T !x' _aren't_ exactly the same in Haskell, they _could_ have been, and then 'newtype' would have been the perfect replacement for 'data'. The fact that 'newtype' and 'data' *both* introduce new types is surely the one confusing point here. But again, this makes 'data' the confusing keyword, not 'type' and not 'newtype'. More precisely, we could have had something like newtype T x = ~T !x {- current newtype -} newtype C x = A | B x | C x x { - current data -} or some such distinction. But we have what we have, and it certainly isn't *more* confusing than F# or Clean. If this is the worst problem beginners have, Haskell must be in wonderful shape.
It's just like if you write
answer = 42
This does NOT define a new constant. It gives a new name for an existing value. Next thing someone will say that this ought to be written
value_alias answer = 42
or something.
But with this point of view 'type Sequence t = [t]' is also somehow bogus and should just be 'Sequence t = [t]'.
WRONG. Type names and function names live in different namespaces. 'type' is NECESSARY to specify the namespace that Sequence lives in.

"Richard A. O'Keefe"
But we have what we have, and it certainly isn't *more* confusing than F# or Clean. If this is the worst problem beginners have, Haskell must be in wonderful shape.
This isn't supposed to be a contest of any sort, in my mind, but rather an attempt to improve things, in absolute terms. The way you dissected the type/newtype/data trinity.. I must say I like it best. The distinction between 'type' and 'newtype' really ought to be underscored, and in this context 'data' is what stands out, indeed. -- с уважениeм / respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

It sort of should I think. In Idris I believe closed type families just use regular function syntax, can be declared inside of let-bindings, have type signatures themselves, and otherwise syntactically behave just like the value level equivalents. I'm hoping at some point goldfire's dependent Haskell work will eventually lead to the same in Haskell, though I know from his comments on reddit that this is very far off from being a reality. Daniel Trstenjak-2 wrote
But with this point of view 'type Sequence t = [t]' is also somehow bogus and should just be 'Sequence t = [t]'.
Greetings, Daniel _______________________________________________ Haskell-Cafe mailing list
Haskell-Cafe@
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- View this message in context: http://haskell.1045720.n5.nabble.com/an-idea-for-modifiyng-data-newtype-synt... Sent from the Haskell - Haskell-Cafe mailing list archive at Nabble.com.

On 18 août 2015, at 08:16, Richard A. O'Keefe
If you want to argue about the grammar of 'data' declarations, go right ahead. I myself was a little confused by them at the beginning, because the declare a new type and some constructors, but do not declare 'data' in the sense that a DATA statement in BASIC or Fortran or the DATA DIVISION in COBOL does. The word 'data' is so very far from conveying the idea "introduce a new type" that it's not funny.
Ok, then, just hypothetically (i do not want to argue about actually introducing it), how about type Name = String type Date ::= Date Int Int Int type Anniversary ::= Birthday Name Date | Wedding Name Name Date instead of type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date ? Alexey.

Anybody who comes acros "data _ = _" and feels entitled to understand it
before looking up the data keyword is being too lazy.
On Wed, Aug 19, 2015 at 1:09 AM, Alexey Muranov
On 18 août 2015, at 08:16, Richard A. O'Keefe
wrote: If you want to argue about the grammar of 'data' declarations, go right ahead. I myself was a little confused by them at the beginning, because the declare a new type and some constructors, but do not declare 'data' in the sense that a DATA statement in BASIC or Fortran or the DATA DIVISION in COBOL does. The word 'data' is so very far from conveying the idea "introduce a new type" that it's not funny.
Ok, then, just hypothetically (i do not want to argue about actually introducing it), how about
type Name = String type Date ::= Date Int Int Int type Anniversary ::= Birthday Name Date | Wedding Name Name Date
instead of
type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date
?
Alexey. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Funnily, i've just noticed that this notation with `::=` to define data types is used in [Why Functional Programming Matters by John Hughes](http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html). Just a remark. Alexey. On Saturday, August 8, 2015 at 1:09:24 PM UTC+2, Alexey Muranov wrote:
Hello,
i would like to suggest an idea for modifying the basic data/newtype syntax in Haskell: replace the equality sign `=` with `::=`.
When i started learning Haskell, the most confusing part of the syntax for me was the equality sign in `data` definition. I could not even guess what the `data` definition meant without reading a chapter or two about types in Haskell, and i think it was partially due to the equality sign. I still find this notation inconsistent with other uses of the equality sign in Haskell and in general.
For example, in
type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date
the second line is particularly disorienting IMO because on two sides of the equality, `Date` denotes different things.
As far as i understand, in all contexts except those of `data` and `newtype` definitions, the equality sign in Haskell denotes the actual equality for all purposes: if a line
foo x y = bar y x
is present in a program, `foo a b` and `bar b a` can be used more or less interchangeably elsewhere in the program. Similarly, if the line
type Name = String
is present, `Name` can be used as `String`. Clearly, the equality in
data Date = Date Int Int Int
does not have such property.
I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of
type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date
IMO this would make the program easier to read in general and the difference between `type` and `newtype` more clear. Maybe the can even make the use of keywords redundant, by allowing to write simply
Name = String Date ::= Date Int Int Int Anniversary ::= Birthday Name Date | Wedding Name Name Date
What do you think?
Alexey. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On Sat, Aug 8, 2015 at 4:39 PM, Alexey Muranov
Hello,
i would like to suggest an idea for modifying the basic data/newtype syntax in Haskell: replace the equality sign `=` with `::=`.
When i started learning Haskell, the most confusing part of the syntax for me was the equality sign in `data` definition. I could not even guess what the `data` definition meant without reading a chapter or two about types in Haskell, and i think it was partially due to the equality sign. I still find this notation inconsistent with other uses of the equality sign in Haskell and in general.
For example, in
type Name = String data Date = Date Int Int Int data Anniversary = Birthday Name Date | Wedding Name Name Date
the second line is particularly disorienting IMO because on two sides of the equality, `Date` denotes different things.
As far as i understand, in all contexts except those of `data` and `newtype` definitions, the equality sign in Haskell denotes the actual equality for all purposes: if a line
foo x y = bar y x
is present in a program, `foo a b` and `bar b a` can be used more or less interchangeably elsewhere in the program. Similarly, if the line
type Name = String
is present, `Name` can be used as `String`. Clearly, the equality in
data Date = Date Int Int Int
does not have such property.
I think that if `::=` was used instead of `=` in `data` and `newtype` definitions, this would suggest to a newcomer that the syntax of the two sides might be different, and would helpfully remind of the Backus–Naur Form for syntax rules. I think that a newcomer to Haskell, like myself, would have had a better chance of guessing the meaning of
type Name = String data Date ::= Date Int Int Int data Anniversary ::= Birthday Name Date | Wedding Name Name Date
IMO this would make the program easier to read in general and the difference between `type` and `newtype` more clear. Maybe the can even make the use of keywords redundant, by allowing to write simply
Name = String Date ::= Date Int Int Int Anniversary ::= Birthday Name Date | Wedding Name Name Date
What do you think?
I taught functional programming (with gofer) some decades ago. I found some of your points (and some more) slowed down students sufficiently that making small changes in gofer was worth the effort My changes are described here http://www.the-magus.in/Publications/notation.pdf The appendix at end summarises the changes. Note that the ctype keyword (concrete-type) as replacement for 'data' predates GADT by about a decade though I came to it from a pedagogy not a generality angle If someone wants to try it, this modified gofer is at https://github.com/rusimody/gofer Note: I am really not entering the debate that Haskell should be changed Regards Rusi -- http://www.the-magus.in http://blog.languager.org

I note that Haskell is not the only language to link identifiers with types using '::' rather than ':'. Apart from the obvious (Clean), there's Fortran. From the file ATMDYN.f in a system that NASA helpfully make available, LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE This puts the type before the identifiers, but still... Didn't Miranda use '::=' to declare sum-of-product types?
participants (24)
-
Alexey Muranov
-
amindfv@gmail.com
-
Bardur Arantsson
-
Brandon Allbery
-
Corentin Dupont
-
Daniel Trstenjak
-
Geraldus
-
Hilco Wijbenga
-
htebalaka
-
Jeffrey Brown
-
Jonas Scholl
-
Kosyrev Serge
-
Lars Hupel
-
M Farkas-Dyck
-
MigMit
-
Mike Meyer
-
Oliver Charles
-
Richard A. O'Keefe
-
Richard Eisenberg
-
Rustom Mody
-
Sven Panne
-
Tom Ellis
-
Vlatko Basic
-
Will Yager