![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
http://haskell.org/haskellwiki/Shooting_your_self_in_the_foot
...but then, most of these will probably be a little lost here. :-/
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible wrote:
> http://haskell.org/haskellwiki/Shooting_your_self_in_the_foot
>
> ...but then, most of these will probably be a little lost here. :-/
I see too much repeated stuff in that list... The "you shoot yourself but
nothing happens until you look at the foot" is like half a dozen times with
different wording.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Nicolas Alvarez wrote:
> I see too much repeated stuff in that list... The "you shoot yourself but
> nothing happens until you look at the foot" is like half a dozen times with
> different wording.
Agreed.
When you word it correctly, it's amusing. When you repeat it 25 times
with slightly different wording... it quickly stops being amusing.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Nicolas Alvarez <nic### [at] gmail com> wrote:
> I see too much repeated stuff in that list... The "you shoot yourself but
> nothing happens until you look at the foot" is like half a dozen times with
> different wording.
Which raises an interesting question: Can lazy evaluation hide bugs
which could otherwise be immediately detected?
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
And by the way... The "nothing happens until you try to see it" reminds me
of quantum stuff :P
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Nicolas Alvarez wrote:
> And by the way... The "nothing happens until you try to see it" reminds me
> of quantum stuff :P
You're not the only one. ;-)
In Haskell, "cat" may be alive or dead (or neither), and it doesn't
became a definite value until you "observe" it.
(And if you never "observe" it, it may in a very real sense never exist.
The compiler might optimise it out of existence if it isn't used for
anything...)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Which raises an interesting question: Can lazy evaluation hide bugs
> which could otherwise be immediately detected?
That *is* an interesting question.
Suppose you write a tokeniser that takes a string and returns a list of
tokens. Suppose your program just counts how many tokens there are. You
run your program, and it seems to work. Great!
Then later you change it so it tries to print out the tokens. And *now*
you discover that processing the 8th token in the list causes a
divide-by-zero error. Oops! If it weren't for lazy evaluation, you would
have found that out sooner.
OTOH, by the simple act of printing out the token list while you're
testing your tokeniser, you would probably have found that bug anyway.
So it's a pretty contrived example.
Let us not forget, of course, that in a "normal" language it's not in
any way unusual for changing the structure of a program to cause some
new, previously unused code path to suddenly get used and reveal a bug
or two that you didn't know about. So lazy evaluation doesn't make
things *so* much different. It just makes it somewhat easier for certain
kinds of bugs to hide.
(Of course, Haskell also makes it easier to test your functions. You
don't have to write a whole test program, just open up GHCi and type in
some expressions to check you get the right answers. And since printing
out a value usually causes it to be completely evaluated, laziness
shouldn't be hiding anything from you...)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible <voi### [at] dev null> wrote:
> Let us not forget, of course, that in a "normal" language it's not in
> any way unusual for changing the structure of a program to cause some
> new, previously unused code path to suddenly get used and reveal a bug
> or two that you didn't know about.
It's just that with lazy evaluation even if you *do* execute all possible
code paths that doesn't guarantee that their result is actually calculated.
Thus code coverage might not have the same degree of guarantee than with
other languages.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> It's just that with lazy evaluation even if you *do* execute all
> possible
> code paths that doesn't guarantee that their result is actually
> calculated. Thus code coverage might not have the same degree of guarantee
> than with other languages.
>
And you can't even turn off lazy evaluation for testing. In Haskell it seems
to be common to generate generate infinite lists and then only read the
needed data from them. Unless you have infinite memory, you *need* lazy
evaluation.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Invisible <voi### [at] dev null> wrote:
>> Let us not forget, of course, that in a "normal" language it's not in
>> any way unusual for changing the structure of a program to cause some
>> new, previously unused code path to suddenly get used and reveal a bug
>> or two that you didn't know about.
>
> It's just that with lazy evaluation even if you *do* execute all possible
> code paths that doesn't guarantee that their result is actually calculated.
> Thus code coverage might not have the same degree of guarantee than with
> other languages.
Depends on what you do with the results.
If you do "nothing" with them then no, they won't be computed. If you,
say, check that the generated result matches an expected result, then
the way equality is *usually* implemented will ensure that the full
result is indeed computed.
If your program is *supposed* to generate an infinite result set then
yeah, you have a problem. You'll have to check that some finite portion
of it matches what it's supposed to be. ;-)
One of the nice properties of Haskell is that since a function can have
no side effects, you "only" need to check that it produces the correct
output for every valid input. (Yeah, I realise that's usually still
impossible - but it beats having to ensure that global variables get
initialised first and aren't corrupted afterwards, checking for thread
safety, etc.)
GHC actually comes with a library called Quick Check, who's purpose is
to automatically generate (randomised) test cases and check arbitrary
properties that you specify. (Obviously they must be *computable*
properties - in fact _you_ have to specify the algorithm for checking
the result is acceptable.) The only snag is that you need to be careful
with how you randomise so you get a "meaningful" sample.
There are people in the Haskell community who are very obsessive about
always writing Quick Check properties for all code developed.
Personally, I sometimes find that devising meaningful tests that are
machine-checkable can be as hard as writing the code I want to check... ;-)
The times I have used Quick Check, it's been quite helpful. For example,
a while back a started work on a small library to do binary
serialisation. (As in, read and write integers of arbitrary width, not
limited to being byte-aligned.) QC has invaluable in quickly generating
lots of random inputs, writing then and reading them back, and checking
the result match. Usually if there was a bug, it found it in split
seconds. Much quicker than typing in tests by hand...
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |