Well, I'm glad to see I provoked some discussion!
Simon writes:
Lennart writes:
| So if we had
|
| data Age = Age !Int
| foo (Age n) = (n, Age (n+1))
|
| it would translate to
|
| foo (MakeAge n) = (n, seq MakeAge (n+1))
|
| [makeAge is the "real" constructor of Age]
Indeed, the (seq MakeAge (n+1) isn't eval'd till the second
component of the pair is. But my point was rather that foo
evaluates its argument (MakeAge n), and hence n, as part of its
pattern matching. Hence foo is strict in n.
Why should foo evaluate its argument? It sounds to me like
Lennart is right, and I should not have let Simon lead me astray!
I think its vital that users know how to declare a new isomorphic
datatype; it is not vital that they understand strictness declarations.
Hence, I favor that
newtype Age = Age Int
data Age = Age !Int
be synonyms, but that both syntaxes exist.
This is assuming I have understood Lennart correctly, and that
foo (Age n) = (n, Age (n+1))
foo' a = (n, Age (n+1)) where (Age n) = a
are equivalent when Age is declared as a strict datatype. Unlike
Sebastian or Simon, I believe it would be a disaster if for a newtype
one had to distinguish these two definitions.
Cheers, -- P