Re: [Haskell-cafe] Is take behaving correctly?

2007-09-13 Thread rahn



pref_eq k xs ys = take k xs == take k ys



This seems to be a straightforward implementation with good properties.



Actually, no, at least not if implemented naively.


I choosed this example, since I stumbled on this question last week.  
Reputable mathematicians doing combinatorics on words are using  
exactly this definition! (Karhumäki, Harju) There are others (Holub),  
that use the (to the computer scientist) nicer(?)


pref_eq 0 _  _  = True
pref_eq _ _  [] = False
pref_eq _ [] _  = False
pref_eq k (x:xs) (y:ys) = x == y  pref_eq (k-1) xs ys

And as you guess it, there are lemmata (and probably theorems), that  
hold for one of the definitions only... Later, the mathematicians  
agree upton to call the first version pref_eq and the second  
pref_eq_proper.


And yes, you are right, just to change the behavior of take would not  
solve this issue. My topic was really more like



don't leap into coding a function before you know what it means


as you pointed out with nice words :-) This is not the main topic of  
the thread (is this true?) but we are in a cafe, so from time to time  
one adds some cents...


/BR
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-12 Thread rahn



take 1000 [1..3] still yields [1,2,3]


You can think about take n as: Take as much as possible, but at most n  
elements. This behavior has some nice properties as turned out by  
others, but there are some pitfalls. We have


length . take n /= const n

in general, instead only

length . take n `elem` map const [0..n]

holds. Therefore head . length n is unsafe for all n, even strict  
positive ones. Moreover, it is easy to produce tricky code. Assume you  
want to know, whether the prefixes of length k are equal and write


pref_eq k xs ys = take k xs == take k ys

This seems to be a straightforward implementation with good  
properties. Now play a bit with it:


Prelude pref_eq 3 ab abc
False
Prelude pref_eq 3 ab ab
True
Prelude map (pref_eq 3 ab) $ Data.List.inits abc
[False,False,True,False]

Uhh, this is not what everybody expects at first glance. In particular, if

pref_eq k xs ys == False

then *not* necessarily

pref_eq k xs (init ys) == False.

As always, quickCheck is your friend to assure (or reject) such a property.

/BR
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-12 Thread Conor McBride

Hi folks

On 12 Sep 2007, at 00:38, Brent Yorgey wrote:



On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote: Hi
take 1000 [1..3] still yields [1,2,3]
I thought it was supposed to return an error.


[..]

If for some reason you want a version that does return an error in  
that situation, you could do something like the following:


take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
   | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

I'm not sure why you'd want that, though.  The standard  
implementation gracefully handles all inputs, and usually turns out  
to be what you want.


There are two sides to this form of grace, though. I'll grant you,  
it's

quite hard to pull off a successful fraud without a degree of aplomb.

I always hope that key invariants and hygiene conditions can be built  
into
the static semantics of programs, but where that's impractical  
somehow, I
prefer if the dynamic behaviour makes it as easy as possible to  
assign the

blame for errors. In such circumstances, I'd like operations to complain
about bogus input, rather than producing bogus output.

These GIGO problems do bite in real life. I spent a long time finding  
a bug

in somebody else's typechecker which boiled down to the silly mistake of
zipping the wrong lists together. The right lists were guaranteed to  
match
in length, but there was no reason for the wrong lists to do so.  
Problem:
unless you were doing fairly wacky stuff, they both happened to have  
length

zero. Once weirder things arrived, the discrepancies showed up and the
truncations started happening, causing well-formed but ill-typed  
expressions

to be generated by and propagated around the kernel of the system, which
eventually choked in some essentially random place. We had many suspects
before we found the culprit. If the programmer had used a version of zip
which bombed in off-diagonal cases, we'd have been straight there.

So I might very well consider having more than one version of take  
around,

depending on my requirements for its use. I might even consider the more
informative function which also returns the length of the shortfall.
In a dependently typed setting, I wouldn't write take and drop: I'd  
equip

lists with a view incorporating both, guaranteeing the length invariants
and that the sublists actually append to the input. But where shame is
unattainable, blame is really quite important.

All the best

Conor

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-12 Thread Henning Thielemann


On Wed, 12 Sep 2007, Conor McBride wrote:


Hi folks

On 12 Sep 2007, at 00:38, Brent Yorgey wrote:


On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote: Hi
take 1000 [1..3] still yields [1,2,3]
I thought it was supposed to return an error.


[..]

If for some reason you want a version that does return an error in that 
situation, you could do something like the following:


take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
  | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

I'm not sure why you'd want that, though.  The standard implementation 
gracefully handles all inputs, and usually turns out to be what you want.


I always hope that key invariants and hygiene conditions can be built into
the static semantics of programs, but where that's impractical somehow, I
prefer if the dynamic behaviour makes it as easy as possible to assign the
blame for errors. In such circumstances, I'd like operations to complain
about bogus input, rather than producing bogus output.


Seconded. The List functions have some kind of scripting language 
semantics or C semantics, that is, consume almost every input, 
regardless of the meaning of the output, just in order to avoid error 
messages. Indeed there are some usages like taking a prefix of an infinite 
list (zip abc [1..]), where the current behaviour of 'zip' is useful. 
However it would be better to have versions of 'zip' to support these 
special cases. Ideally the equal length could be expressed in types, like


 zipInfInf :: InfiniteList a - InfiniteList b - InfiniteList (a,b)
 zipInf:: InfiniteList a - FiniteList n b - FiniteList n (a,b)
 zip   :: FiniteList n a - FiniteList n b - FiniteList n (a,b)  ,

which would need dependent types.


These GIGO problems do bite in real life. I spent a long time finding a bug
in somebody else's typechecker which boiled down to the silly mistake of
zipping the wrong lists together. The right lists were guaranteed to match
in length, but there was no reason for the wrong lists to do so.


I had a bug which was due to the wrong order of applications of 'filter' 
and 'zip'. I had two lists 'a' and 'b' of the same length, where 'b' 
contained a condition for filtering, then I wrote

  zip a (filter p b)
 instead of
  filter (p . snd) $ zip a b
 The wrong version was temptingly short, but it matched the wrong 
elements. The bug had been catched early, if 'zip' would have checked the 
length of its inputs.

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-12 Thread Dan Weston

One idiom I rely on pretty often is

   (id  tail)  uncurry (zipWith f)

to do pairwise binary operations (though I suspect an Applicative 
functor might be a better way to go?).


E.g. my solution for ProjectEuler problem #18 (max sum of vertical path 
through a triangle of integers) is:


 f = curry $ second ((id  tail)   
 uncurry (zipWith max))  
 uncurry (zipWith (+) )

 zeroes   = repeat 0
 maxTotal = foldr f zeroes

Key to this idiom is that zipWith stops after the shortest element. 
Otherwise, I'd need an extra init function and have to test for empty lists.


So there is at least one good reason for the way zipWith works.

Dan Weston

Conor McBride wrote:

Hi folks

On 12 Sep 2007, at 00:38, Brent Yorgey wrote:



On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote: Hi
take 1000 [1..3] still yields [1,2,3]
I thought it was supposed to return an error.


[..]

If for some reason you want a version that does return an error in 
that situation, you could do something like the following:


take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
   | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

I'm not sure why you'd want that, though.  The standard implementation 
gracefully handles all inputs, and usually turns out to be what you want.


There are two sides to this form of grace, though. I'll grant you, it's
quite hard to pull off a successful fraud without a degree of aplomb.

I always hope that key invariants and hygiene conditions can be built into
the static semantics of programs, but where that's impractical somehow, I
prefer if the dynamic behaviour makes it as easy as possible to assign the
blame for errors. In such circumstances, I'd like operations to complain
about bogus input, rather than producing bogus output.

These GIGO problems do bite in real life. I spent a long time finding a bug
in somebody else's typechecker which boiled down to the silly mistake of
zipping the wrong lists together. The right lists were guaranteed to match
in length, but there was no reason for the wrong lists to do so. Problem:
unless you were doing fairly wacky stuff, they both happened to have length
zero. Once weirder things arrived, the discrepancies showed up and the
truncations started happening, causing well-formed but ill-typed 
expressions

to be generated by and propagated around the kernel of the system, which
eventually choked in some essentially random place. We had many suspects
before we found the culprit. If the programmer had used a version of zip
which bombed in off-diagonal cases, we'd have been straight there.

So I might very well consider having more than one version of take around,
depending on my requirements for its use. I might even consider the more
informative function which also returns the length of the shortfall.
In a dependently typed setting, I wouldn't write take and drop: I'd equip
lists with a view incorporating both, guaranteeing the length invariants
and that the sublists actually append to the input. But where shame is
unattainable, blame is really quite important.

All the best

Conor

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe





___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-12 Thread ok

On 12 Sep 2007, at 8:08 pm, [EMAIL PROTECTED] wrote:




take 1000 [1..3] still yields [1,2,3]


You can think about take n as: Take as much as possible, but at  
most n elements. This behavior has some nice properties as turned  
out by others, but there are some pitfalls.


One of the very nice properties is this:
  for all n = 0, m = 0
 take m . take n = take (m+n)
 drop m . drop n = drop (m+n)
And another is this:
  for all xs, n = 0
 take n xs ++ drop n xs == xs


length . take n /= const n

in general, instead only

length . take n `elem` map const [0..n]


What we have is
  for all xs, n = 0
n = length xs = length (take n xs) == n

Suppose we had the unsafe take.  Would we then have

   length . take n == const n?

NO!  For some xs, (length . take n) xs would be bottom,
while (const n) xs would be n.  As far as I can see, the
strongest statement about length that unsafeTake would
satisfy is

  for all xs, n = 0
n = length xs = length (unsafeTake n xs) == n

which is, mutatis mutandis, the SAME law that applies to the
`take` we already have.


Therefore head . length n is unsafe for all n, even strict positive  
ones.


I'm assuming that should have been (head . take n).
The thing is that (head . take n) fails when and only when
(head . unsafeTake n) fails; the only difference is which
function reports the error.

Moreover, it is easy to produce tricky code. Assume you want to  
know, whether the prefixes of length k are equal and write


pref_eq k xs ys = take k xs == take k ys

This seems to be a straightforward implementation with good  
properties.

Actually, no, at least not if implemented naively.  (I wonder just how
clever GHC is with this code?  I can't check at the moment.  After
heroic efforts by my sysadmin, I was finally able to install the binary
release of GHC 6.6.1, but (a) it assumes that gcc accepts a -fwrapv
flag, which gcc 3.3.2 (TWW) does not accept, and (b) after patching
that, gcc still chokes on the output of GHC.)

The obvious question is what pref_eq k xs ys *should* do when xs
or ys is not at least k long.  Does it mean I believe xs and ys
are at least k long: if they are, check their prefixes, if they
aren't, raise an error or does it mean xs and ys are at least
k long and their prefixes of length k are equal?

Uhh, this is not what everybody expects at first glance. In  
particular, if


pref_eq k xs ys == False

then *not* necessarily

pref_eq k xs (init ys) == False.

As always, quickCheck is your friend to assure (or reject) such a  
property.


That is certainly so, but the fundamental issue in this example is
not what take does, but don't leap into coding a function before
you know what it means.  We've now seen three readings of what
pref_eq means.  In particular,

pref_eq k xs ys = unsafeTake k xs == unsafeTake k ys

would NOT satisfy the law that
pref_eq k xs ys == False
 = pref_eq k xs (init ys) == False
because pref_eq 2 [1,2] [1,3] == False
but pref_eq 2 [1,2] [1]   = undefined

So fixing take would not, in this case, produce the desired behaviour.

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread PR Stanley

Hi
take 1000 [1..3] still yields [1,2,3]
I thought it was supposed to return an error.
Any ideas?
Thanks, Paul

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Tim Chevalier
On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
 Hi
 take 1000 [1..3] still yields [1,2,3]
 I thought it was supposed to return an error.
 Any ideas?

No, that's the behavior for take specified in the Haskell 98 report:
http://haskell.org/onlinereport/standard-prelude.html
-- take n, applied to a list xs, returns the prefix of xs of length n,
-- or xs itself if n  length xs.

Cheers,
Tim

-- 
Tim Chevalier * catamorphism.org * Often in error, never in doubt
Modesty...is both alien and irrelevant to people who are happy in
themselves, in their beings, in their skins, their natures, their
capacities.--Anne Sayre
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Brent Yorgey
On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:

 Hi
 take 1000 [1..3] still yields [1,2,3]
 I thought it was supposed to return an error.
 Any ideas?
 Thanks, Paul


If for some reason you want a version that does return an error in that
situation, you could do something like the following:

take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
   | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

I'm not sure why you'd want that, though.  The standard implementation
gracefully handles all inputs, and usually turns out to be what you want.
Really, if I were you, instead of making a version take' as above, I would
just use the standard take but check for the length of the list in the
places where it matters.

-Brent
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread PR Stanley

I suppose I'm thinking of head or tail - e.g. head [] or tail [].
I'm trying to write my own version of the find function. I have a few 
ideas but not quite sure which would be more suitable in the context of FP.
Any advice would be gratefully received - e.g. do I use recursion, 
list comprehension or what?

Thanks, Paul

At 00:08 12/09/2007, you wrote:

On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
 Hi
 take 1000 [1..3] still yields [1,2,3]
 I thought it was supposed to return an error.
 Any ideas?

No, that's the behavior for take specified in the Haskell 98 report:
http://haskell.org/onlinereport/standard-prelude.html
-- take n, applied to a list xs, returns the prefix of xs of length n,
-- or xs itself if n  length xs.

Cheers,
Tim

--
Tim Chevalier * catamorphism.org * Often in error, never in doubt
Modesty...is both alien and irrelevant to people who are happy in
themselves, in their beings, in their skins, their natures, their
capacities.--Anne Sayre
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Don Stewart
byorgey:
On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
 
  Hi
  take 1000 [1..3] still yields [1,2,3]
  I thought it was supposed to return an error.
  Any ideas?
  Thanks, Paul
 
If for some reason you want a version that does return an error in that
situation, you could do something like the following:
 
take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
   | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

And we'd call it unsafeTake, just like unsafeFromJust and unsafeTail :-)

-- Don
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Brent Yorgey
On 9/11/07, Don Stewart [EMAIL PROTECTED] wrote:

 byorgey:
 On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
 
   Hi
   take 1000 [1..3] still yields [1,2,3]
   I thought it was supposed to return an error.
   Any ideas?
   Thanks, Paul
 
 If for some reason you want a version that does return an error in
 that
 situation, you could do something like the following:
 
 take' n _ | (n = 0) = []
 take' n [] | (n  0) = error take': list too short
| otherwise = []
 take' n (x:xs) = x : take' (n-1) xs

 And we'd call it unsafeTake, just like unsafeFromJust and unsafeTail :-)

 -- Don


Hmm, that's funny, I don't recall ever hearing of those functions... =)

ooo, Don has a shiny new e-mail address!

-Brent
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread John Meacham
On Tue, Sep 11, 2007 at 07:38:18PM -0400, Brent Yorgey wrote:
 On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
 
  Hi
  take 1000 [1..3] still yields [1,2,3]
  I thought it was supposed to return an error.
  Any ideas?
  Thanks, Paul
 
 
 If for some reason you want a version that does return an error in that
 situation, you could do something like the following:
 
 take' n _ | (n = 0) = []
 take' n [] | (n  0) = error take': list too short
| otherwise = []
 take' n (x:xs) = x : take' (n-1) xs


you could also do something like

take' n xs = take n (xs ++ error I want more!)


John

-- 
John Meacham - ⑆repetae.net⑆john⑈
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread PR Stanley

Let me get this right, are you saying it's unsafe when it returns an error?
Paul

At 00:40 12/09/2007, you wrote:

byorgey:
On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:

  Hi
  take 1000 [1..3] still yields [1,2,3]
  I thought it was supposed to return an error.
  Any ideas?
  Thanks, Paul

If for some reason you want a version that does return an error in that
situation, you could do something like the following:

take' n _ | (n = 0) = []
take' n [] | (n  0) = error take': list too short
   | otherwise = []
take' n (x:xs) = x : take' (n-1) xs

And we'd call it unsafeTake, just like unsafeFromJust and unsafeTail :-)

-- Don
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Don Stewart
prstanley:
I suppose I'm thinking of head or tail - e.g. head [] or tail [].
I'm trying to write my own version of the find function. I have a few
ideas but not quite sure which would be more suitable in the context of
FP.
Any advice would be gratefully received - e.g. do I use recursion, list
comprehension or what?

Well, you want to filter all elements from a list that match a predicate, 
and then return Just the first match, or Nothing?

find :: (a - Bool) - [a] - Maybe a

Checking the current behaviour:

 find isSpace haskell is fun
Just ' '

My first go would be something like this: ok, so let's start with 'filter':

 filter Char.isSpace haskell is fun
  

Good, then the natural translation to the Maybe type:

 listToMaybe . filter isSpace $ haskell is fun
Just ' '

And we're almost done. Just firing up QuickCheck:

 quickCheck $ \p (xs :: [Int])  - find p xs == listToMaybe (filter p 
xs)
OK, passed 100 tests.

Seems ok.

I hope that gives some insight into the process of deriving Haskell 
implementations by building up a pipeline of pieces.

-- Don

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Don Stewart
byorgey:
On 9/11/07, Don Stewart [EMAIL PROTECTED] wrote:
 
  byorgey:
  On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
  
Hi
take 1000 [1..3] still yields [1,2,3]
I thought it was supposed to return an error.
Any ideas?
Thanks, Paul
  
  If for some reason you want a version that does return an error in
  that
  situation, you could do something like the following:
  
  take' n _ | (n = 0) = []
  take' n [] | (n  0) = error take': list too short
 | otherwise = []
  take' n (x:xs) = x : take' (n-1) xs
 
  And we'd call it unsafeTake, just like unsafeFromJust and unsafeTail :-)
 
  -- Don
 
Hmm, that's funny, I don't recall ever hearing of those functions... =)
 
ooo, Don has a shiny new e-mail address!
 

And a shiny new job in a shiny new city :-)

-- Don
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Don Stewart
prstanley:
Let me get this right, are you saying it's unsafe when it returns an
error?

Partial functions may crash your program, so that's unsafe by some definitions, 
yep.
We have tools that analyse programs for such bugs, in fact (Neil's `catch' 
program).

-- Don
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is take behaving correctly?

2007-09-11 Thread Derek Elkins
On Tue, 2007-09-11 at 16:48 -0700, Don Stewart wrote:
 byorgey:
 On 9/11/07, Don Stewart [EMAIL PROTECTED] wrote:
  
   byorgey:
   On 9/11/07, PR Stanley [EMAIL PROTECTED] wrote:
   
 Hi
 take 1000 [1..3] still yields [1,2,3]
 I thought it was supposed to return an error.
 Any ideas?
 Thanks, Paul
   
   If for some reason you want a version that does return an error in
   that
   situation, you could do something like the following:
   
   take' n _ | (n = 0) = []
   take' n [] | (n  0) = error take': list too short
  | otherwise = []
   take' n (x:xs) = x : take' (n-1) xs
  
   And we'd call it unsafeTake, just like unsafeFromJust and unsafeTail 
  :-)
  
   -- Don
  
 Hmm, that's funny, I don't recall ever hearing of those functions... =)
  
 ooo, Don has a shiny new e-mail address!
  
 
 And a shiny new job in a shiny new city :-)

In a   new country.

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe