Re: learning to love laziness

2003-09-26 Thread Derek Elkins
On Thu, 25 Sep 2003 12:59:37 -0700
Mark Tullsen [EMAIL PROTECTED] wrote:

 Haskell has lazy/lifted products and not true products. 

Aren't lazy products true products?  What makes something a product is:
fst (x,y) = x
snd (x,y) = y 
for all x and y.  This holds with lazy products but not eager ones 
fst (3,_|_) = _|_ /= 3

I'd think the problem is that Haskell isn't consistently lazy (for good
reason!).  Haskell has eager coproducts (case analysis).  If you didn't
use pattern matching (or used lazy pattern matching), then this problem
wouldn't come up.  Of course, lazy coproducts (pattern matching) is
somewhat uninteresting (f ~True = 1;f ~False = 2; f False == 1)

 Maybe from seeing this, it's clearer why laws such as
x = (fst x,snd x)
 do not hold.  Neither does the following law hold
 (uncurry . curry) f = f
 which is unfortunate (for a language named after Haskell *Curry*).
 To see why it doesn't hold, compare t1 and t2 in this program:
f (_,_) = 1
t1 = f undefined
t2 = (uncurry . curry) f undefined

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: learning to love laziness

2003-09-26 Thread John Hughes
On Fri, 26 Sep 2003, Derek Elkins wrote:

 On Thu, 25 Sep 2003 12:59:37 -0700
 Mark Tullsen [EMAIL PROTECTED] wrote:

  Haskell has lazy/lifted products and not true products.

 Aren't lazy products true products?  What makes something a product is:
 fst (x,y) = x
 snd (x,y) = y
 for all x and y.  This holds with lazy products but not eager ones
 fst (3,_|_) = _|_ /= 3


Right, strict languages do not have true products either. A strict
language without side-effects, and in which no expression can loop
infinitely or cause an error (because these are side-effects of a sort),
COULD have true products.

The reason Haskell doesn't is that true categorical products have to
satisfy an additional law,

(fst x, snd x) = x

and Haskell's products don't, since

(_|_, _|_) /= _|_

The key question is: can we write a program which distinguishes these two
values? And we certainly can, for example

seq (_|_,_|_) 0 =  0
seq_|_0 = _|_

or

case (_|_,_|_) of (_,_) - 0=  0
case_|_of (_,_) - 0= _|_

But of course, *fst and snd* can't distinguish the two! So if we only
operated on products using fst and snd, then we could pretend that these
two values *were* equal, even though they're not in the implementation,
and thus say that Haskell had true products.

This leads some people to suggest that the behaviour of seq and pattern
matching be changed, so as not to distinguish these two values either.
A possible change would be to make both seq and case lazy for product
types, so that if x were a pair, then seq x y would NOT force the
evaluation of x, and case x of (a,b) - e would behave as case x of ~(a,b)
- e, i.e. x would be demanded only when one of a or b was needed.

This would be a good thing, in that Haskell would have a nicer algebra,
but a bad thing, in that it would conceal a difference which is really
present in the implementation, thus denying the programmer a degree of
control over what the implementation does. Namely, a programmer would no
longer be able to move the evaluation of a pair earlier than its
components are needed, by using a seq or a case (or in any other way).

Unfortunately, that control may sometimes be vitally important. Anyone who
has removed space leaks from Haskell programs knows the importance of
controlling evaluation order, so as, for example to drop pointers to
garbage earlier. Consider an expression such as

if ...condition containing the last pointer
  to a very large datastructure...
then (a,b)
else (c,d)

where a, b, c and d are perhaps expressions which *build* a very large
data-structure. Evaluating this expression early enables a lot of garbage
to be collected, but evaluating a component early allocates a lot of
memory before it is needed. Thus it's important to be able to force just
the pair structure, without forcing components, and this distinguishes _|_
from (_|_,_|_), however it is done.

Space debugging does, to quite a large extent, involve inserting seq in
judiciously chosen places. These places are hard to predict in advance
(before seeing the heap profile). If Haskell had true products, and it
turned out that the place needing a seq happened to have a pair type, then
that simple fix would not be applicable. The programmer would need to
refactor the program, replacing the pair by another type representing a
lifted product, before the space leak could be fixed. That might require
changing an arbitrarily large part of the program, which is clearly not
desirable.

So there are good arguments both for and against having true products.
The debate has raged many times during the Haskell design process. In the
end, the argument that Haskell's designers should not make finding and
fixing space leaks any harder than it already is won, and I think that was
the right decision -- but it is a trade-off, and one must recognise that.

Incidentally, exactly the same problem arises for functions: Haskell does
not have true functions either, because _|_ and \x - _|_ are not equal.

John Hughes
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: learning to love laziness

2003-09-25 Thread Mark Tullsen
Haskell has lazy/lifted products and not true products.  This feature
is considered by many to be an unfortunate aspect of Haskell.  A 2-tuple
is just syntactic sugar for
  data Tuple2 a b = Tuple2 a b
Maybe from seeing this, it's clearer why laws such as
  x = (fst x,snd x)
do not hold.  Neither does the following law hold
   (uncurry . curry) f = f
which is unfortunate (for a language named after Haskell *Curry*).
To see why it doesn't hold, compare t1 and t2 in this program:
  f (_,_) = 1
  t1 = f undefined
  t2 = (uncurry . curry) f undefined
- Mark

On Wednesday, September 24, 2003, at 02:07  PM, Norman Ramsey wrote:
Consider the following Haskell function:

asPair x = (fst x, snd x)
This function has type forall a b. (a, b) - (a, b)
and is almost equivalent to the identity function, except it
can be used to make programs terminate that might otherwise fall
into a black hole.
My students are extremely mystified by such functions---and I can 
hardly
blame them!  Is there a good place to read about programming with lazy
evaluation that will cover such functions and when to use them?

Norman
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


learning to love laziness

2003-09-24 Thread Norman Ramsey
Consider the following Haskell function:

 asPair x = (fst x, snd x)

This function has type forall a b. (a, b) - (a, b)
and is almost equivalent to the identity function, except it
can be used to make programs terminate that might otherwise fall
into a black hole.

My students are extremely mystified by such functions---and I can hardly
blame them!  Is there a good place to read about programming with lazy
evaluation that will cover such functions and when to use them?


Norman
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: learning to love laziness

2003-09-24 Thread Richard Nathan Linger


On Wed, 24 Sep 2003, Norman Ramsey wrote:

 Consider the following Haskell function:
 
  asPair x = (fst x, snd x)
 
 This function has type forall a b. (a, b) - (a, b)
 and is almost equivalent to the identity function, except it
 can be used to make programs terminate that might otherwise fall
 into a black hole.
 

What is an example program that asPair rescues from nontermination?

Nathan


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: learning to love laziness

2003-09-24 Thread Iavor Diatchki
hello,

Richard Nathan Linger wrote:
On Wed, 24 Sep 2003, Norman Ramsey wrote:


Consider the following Haskell function:


asPair x = (fst x, snd x)
This function has type forall a b. (a, b) - (a, b)
and is almost equivalent to the identity function, except it
can be used to make programs terminate that might otherwise fall
into a black hole.


What is an example program that asPair rescues from nontermination?
'undefined'  is a program that does not terminate.
'asPair undefined' is a program that terminates immediately (with a pair 
containing two nonterminating components).  unfortunatelu in haskell you 
can tell the difference between those two:

test (x,y) = True

test undefind = undefined
test (asPair undefined) = True
it may be nice to have tuples unlifted in haskell (and in general 
datatypes with a single constructor).  this could be introduced in the 
language by either changing the semantics of 'data' (prolly not too 
nice) or by generalizing 'newtype' (and perhaps renaming it to 'record') 
to handle more than one type.   the semantics of such thing could be 
just like now except that pattern matching on them is always lazy
(i.e.patterns have an implicit ~).  then one could not tell the 
difference between `undefined' and (undefined,undefined).  well i guess 
unless one used seq, but seq is also not very nice.
bye
iavor



--
==
| Iavor S. Diatchki, Ph.D. student   |
| Department of Computer Science and Engineering |
| School of OGI at OHSU  |
| http://www.cse.ogi.edu/~diatchki   |
==
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell