POV-Ray : Newsgroups : povray.off-topic : More random humous Server Time
14 Oct 2026 11:20:17 EDT (-0400)
  More random humous (Message 1 to 30 of 30)  
From: Invisible
Subject: More random humous
Date: 6 Oct 2008 07:32:37
Message: <48e9f755$1@news.povray.org>
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

From: Nicolas Alvarez
Subject: Re: More random humous
Date: 6 Oct 2008 10:18:30
Message: <48ea1e36@news.povray.org>
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

From: Invisible
Subject: Re: More random humous
Date: 6 Oct 2008 10:29:02
Message: <48ea20ae$1@news.povray.org>
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

From: Warp
Subject: Re: More random humous
Date: 6 Oct 2008 10:44:02
Message: <48ea2432@news.povray.org>
Nicolas Alvarez <nic### [at] gmailcom> 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

From: Nicolas Alvarez
Subject: Re: More random humous
Date: 6 Oct 2008 10:51:30
Message: <48ea25f1@news.povray.org>
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

From: Invisible
Subject: Re: More random humous
Date: 6 Oct 2008 10:56:56
Message: <48ea2738$1@news.povray.org>
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

From: Invisible
Subject: Re: More random humous
Date: 6 Oct 2008 11:02:15
Message: <48ea2877$1@news.povray.org>
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

From: Warp
Subject: Re: More random humous
Date: 6 Oct 2008 12:26:21
Message: <48ea3c2c@news.povray.org>
Invisible <voi### [at] devnull> 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

From: Nicolas Alvarez
Subject: Re: More random humous
Date: 6 Oct 2008 12:53:28
Message: <48ea4288@news.povray.org>
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

From: Orchid XP v8
Subject: Re: More random humous
Date: 6 Oct 2008 16:21:17
Message: <48ea733d$1@news.povray.org>
Warp wrote:
> Invisible <voi### [at] devnull> 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

From: Orchid XP v8
Subject: Re: More random humous
Date: 6 Oct 2008 16:39:52
Message: <48ea7798@news.povray.org>
Orchid XP v8 wrote:
> 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.
> 
> Depends on what you do with the results.

Heh, and I'm in such a hurry to reply that I completely miss one of the 
most important points.

No matter how buggy your Haskell code is, it *cannot* segfault. It 
cannot access initialised or dangling references. It cannot corrupt 
global variables. It cannot be thread-unsafe. It cannot cause other 
unrelated parts of your program to not malfunction. You do not need to 
test for these bugs because they cannot exist in the first place.

(Unfortunately most or all of those guarantees vanish as soon as you 
start performing I/O. This perhaps explains why Haskell programs usually 
contain the tiniest amount of explicit I/O possible...)

So even if some code path wasn't adaquately tested, the worst thing your 
program can do is

A) Give the caller the wrong answer.
B) Throw an exception.
C) Lock up and loop forever.
D) Eat all of your RAM.

Exceptions can be caught and logged if desired. It's possible to 
annotate them to get some idea where they're coming from. You can use 
the GHCi debugger to make it automatically drop you into debug mode when 
an exception is thrown. If your program segfaults, there's not much you 
can do about it. It just says "segmentation fault" and stops, and that's it.

The other problems are the same as you'd have in any other language. 
(And they're roughly has difficult to fix as anywhere else.) Admittedly 
the last two problems are somewhat more common in Haskell than in other 
languages. (E.g., it's quite easy to write a function that *should* work 
for an infinite input, but doesn't due to a subtle oversight on your part.)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 6 Oct 2008 16:44:44
Message: <48ea78bb@news.povray.org>
Orchid XP v8 <voi### [at] devnull> wrote:
> No matter how buggy your Haskell code is, it *cannot* segfault. It 
> cannot access initialised or dangling references. It cannot corrupt 
> global variables. It cannot be thread-unsafe. It cannot cause other 
> unrelated parts of your program to not malfunction. You do not need to 
> test for these bugs because they cannot exist in the first place.

  You mean there aren't mutable arrays in Haskell?

-- 
                                                          - Warp


Post a reply to this message

From: Orchid XP v8
Subject: Re: More random humous
Date: 6 Oct 2008 16:58:46
Message: <48ea7c06$1@news.povray.org>
Warp wrote:
> Orchid XP v8 <voi### [at] devnull> wrote:
>> No matter how buggy your Haskell code is, it *cannot* segfault. It 
>> cannot access initialised or dangling references. It cannot corrupt 
>> global variables. It cannot be thread-unsafe. It cannot cause other 
>> unrelated parts of your program to not malfunction. You do not need to 
>> test for these bugs because they cannot exist in the first place.
> 
>   You mean there aren't mutable arrays in Haskell?

You're fascinated by mutable arrays, aren't you? :-)

OK, I rephrase:

   No matter how buggy your Haskell code is, there are a whole bunch of 
Extremely Bad Things that cannot happen --- UNLESS your function uses 
mutable state and/or performs I/O operations. (Or calls external C 
functions. Obviously.)

Happy now? :-P

Haskell does indeed have mutable arrays. And (thread-safe) mutable 
variables of several kinds. There's even a somewhat clunky mutable 
hashtable. (Well let's face it, who would want an *immutable* hashtable??)

You will note that what Haskell *does not* have is mutable global 
variables. So even if you have mutable state, it cannot be a global 
variable. You must manually pass it to any function that wants it. That 
limits the damage somewhat.

(Of course C can have global mutable state, and Haskell can talk to C. 
But that's not really a Haskell property...)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 6 Oct 2008 19:09:52
Message: <48ea9ac0@news.povray.org>
Orchid XP v8 <voi### [at] devnull> wrote:
> >   You mean there aren't mutable arrays in Haskell?

> You're fascinated by mutable arrays, aren't you? :-)

  I suppose I just can't get over the mentality that arrays are the
fastest possible data containers for random access, that random access
is required for very many algorithms, that many algorithms require
modifying the data in the array, and that an array is one of the most
memory-efficient ways of storing data in memory (in certain circumstances
there are even more space-efficient ways, but those are a bit exceptional
cases).

  Many programming languages seem to detest arrays because they are not
very dynamic (it's very hard to increase the size of an array in an
efficient way, and alternative O(1) random access containers which are
easy to resize have an annoying minimum overhead, which is bad for very
small arrays). Of course their mutability (immutable arrays are not very
useful, after all) also makes certain things difficult in a language which
tries to ensure certain things about safety and correctness.

  I'm curious: Were mutable arrays a later addition to the Haskell language?
Did it start as a pure functional language, but mutable arrays were added
later because pure functionality is *not* the silver bullet?-)

-- 
                                                          - Warp


Post a reply to this message

From: nemesis
Subject: Re: More random humous
Date: 7 Oct 2008 00:35:00
Message: <web.48eae686e855c315d4ce4e9c0@news.povray.org>
Warp <war### [at] tagpovrayorg> wrote:
>   I'm curious: Were mutable arrays a later addition to the Haskell language?
> Did it start as a pure functional language, but mutable arrays were added
> later because pure functionality is *not* the silver bullet?-)

Possibly, yes.  Like Monadic IO, which was added later.  You may ask how IO was
done before.

Think Haskell doing pure functional processing and producing and output list of
results and an outer program receiving the list and doing proper, ugly, impure
IO over it and you may have a somewhat accurate picture. Of course, relying on
an external program doesn't do the ego any good... :)

Or so I heard from Simon Peyton Jones in his excelent "Tackling the Awkward
Squad" paper:
http://research.microsoft.com/~simonpj/papers/marktoberdorf/


Post a reply to this message

From: Invisible
Subject: Re: More random humous
Date: 7 Oct 2008 04:38:36
Message: <48eb200c$1@news.povray.org>
>> You're fascinated by mutable arrays, aren't you? :-)
> 
>   I suppose I just can't get over the mentality that arrays are the
> fastest possible data containers for random access, that random access
> is required for very many algorithms, that many algorithms require
> modifying the data in the array, and that an array is one of the most
> memory-efficient ways of storing data in memory (in certain circumstances
> there are even more space-efficient ways, but those are a bit exceptional
> cases).

All valid points of course. (Indeed, in languages like Pascal, you'd be 
forgiven for thinking that arrays are the *only* container type.)

Oddly enough, I don't often find myself wanting to use arrays. I guess 
this is just an artifact of the kinds of programs I tend to write. If I 
was doing lots of image processing or DSP or something then I'd almost 
certainly want arrays. But many of the programs I write involve parsing 
some text input to generate a small parse tree, processing that tree 
according to some highly complex algorithm, and writing the result back 
out. There isn't much call for an array in there. (Although I commonly 
want dictionaries, which you could conceivably implement as hash tables 
instead of trees.)

>   I'm curious: Were mutable arrays a later addition to the Haskell language?
> Did it start as a pure functional language, but mutable arrays were added
> later because pure functionality is *not* the silver bullet?-)

I wasn't there, but as I understand it, Haskell has had mutable arrays 
for a very long time now. (E.g., they are explicitly mentioned in the 
current language standard document, circa 1998.)

Monadic I/O was definitely a later addition; Haskell used to have a much 
more chunky I/O system based on infinite lazy lists. (A Haskell 
"program" takes an infinite list of I/O responses as input, and returns 
an infinite list of I/O requests as output. If that sounds confusing - 
it is. And it's very easy to get wrong!)

As for pure functional programming... it has many attractive properties. 
It is self-evident that a complete program that is *totally* pure is 
formally equivilent to a null program, so that isn't very useful. 
Different functional languages solve this problem in different ways:

- SQL, while not technically a "functional" language, solves it by being 
a special purpose language who's purpose is to return results to the 
caller who then actually "does" stuff with it. (Oh, and by having DML 
statements.)

- Lisp does it by being impure. (I.e., if you want to mutate something, 
you just mutate it. But you're supposed to "avoid" doing that too much.)

- Clean does it using an interesting concept known as "uniqueness 
typing". (Basically works like Lisp, except the type system enforces 
some constraints.)

- Haskell uses monads instead. (It's a simple way to structure I/O, and 
turns out to be useful for many other things too.)

- I have no idea what Erlang does...

Silver bullet? No. Useful? Well, I think so. But then, I'm biased. ;-)


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 7 Oct 2008 06:37:09
Message: <48eb3bd4@news.povray.org>
Orchid XP v8 <voi### [at] devnull> wrote:
> 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.

  Wait a minute... Exactly how do you create a random number generator in
Haskell?

  A random number generator function is, by definition, a function which
returns different values every time it's called (with the exact same
parameters, eg. none), and to do that it must remember its state from
the previous call.

  Not being able to create a RNG function would be rather restrictive in
many applications...

-- 
                                                          - Warp


Post a reply to this message

From: Invisible
Subject: Re: More random humous
Date: 7 Oct 2008 06:45:55
Message: <48eb3de3$1@news.povray.org>
Warp wrote:

>   Wait a minute... Exactly how do you create a random number generator in
> Haskell?
> 
>   Not being able to create a RNG function would be rather restrictive in
> many applications...

You write a function that takes a PRNG state and returns a random number 
and a new PRNG state.

(That is, if you want *psuedo*-random numbers. If you're after truly 
random numbers, you'll obviously need to perform some kind of I/O to get 
them, since true randomness comes only from physical sources.)

This is a common idiom in Haskell; write a function that takes an 
initial state and returns an answer and a new, modified state. In fact, 
there's even a standard monad for automating this so you don't have to 
manually pass the state around by hand all the time.


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 7 Oct 2008 08:17:45
Message: <48eb5368@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> You write a function that takes a PRNG state and returns a random number 
> and a new PRNG state.

  Where do you store this state, given that you can't assign it to anything?

  One common idiom in imperative languages to write a RNG function is,
for example in C++:

namespace
{
    unsigned seed = 0;

    unsigned getRandom(unsigned maxValue)
    {
        seed = <calculate a new seed using seed itself>;
        return seed % maxValue;
    }
}

  Of course you could do it like this:

unsigned getRandom(unsigned seed)
{
    return <calculate a new seed using seed itself>;
}

  But then the calling code would have to keep that seed somewhere. This
can be a real burden if the same RNG stream would need to be used in
different parts of the code (ie. basically the different parts all would
have to share the same seed).

  The idiom used in the POV-Ray SDL is that there's a seed identifier
which internally contains the seed value for that stream. However, the
rand() function *modifies* the contents of the seed identifier in order
to store the newest seed.

  I don't understand how it could be done if modifying the identifier
was prohibited.

-- 
                                                          - Warp


Post a reply to this message

From: Invisible
Subject: Re: More random humous
Date: 7 Oct 2008 08:43:29
Message: <48eb5971$1@news.povray.org>
>> You write a function that takes a PRNG state and returns a random number 
>> and a new PRNG state.
> 
>   Where do you store this state, given that you can't assign it to anything?

It's not so hard.

   get_random :: Seed -> (Int, Seed)

   foo :: Seed -> (Int, Seed)
   foo s0 =
     let
       (x1,s1) = get_random s0
       (x2,s2) = get_random s1
       (x3,s3) = get_random s2
     in ((x1 + x2 + x3) `div` 3, s3)

   bar :: Seed -> ([Int], Seed)
   bar s0 =
     let
       (y1,s1) = foo s0
       (y2,s2) = foo s1
       (y3,s3) = foo s2
     in ([y1, y2, y3], s3)

Here "foo" grabs three random numbers and returns their arithmetic mean. 
(And also returns the final PRNG seed, in case anybody else wants it.) 
Basically you create a new variable for each version of the seed value.

(Similarly, "bar" calls "foo" three times to generate three (more 
uniformly distributed) random numbers and returns them in a list - along 
with the new seed.)

>   But then the calling code would have to keep that seed somewhere. This
> can be a real burden if the same RNG stream would need to be used in
> different parts of the code.

Yes. This does quickly become tedious if you need random numbers all 
over the place. In principle each function that uses it must take an 
initial seed value, and return a new seed value as part of its result. 
And the callers must remember to use the correct version of the state 
each time.

(E.g., imagine if I make a typo and foo actually returns s0 instead of 
s3. Now the "random" number generator appears to always return the same 
result!)

But basically, it works.

You can improve it by using a state monad. This basically means that the 
seed is invisibly passed from place to place automatically (and you 
can't accidentally use the wrong seed by mistake).

Alternatively, you can go down the mutable state road. On the face of 
it, this would still require you to pass around a reference to the 
mutable state, because Haskell doesn't have mutable global variables. So 
some hacking is required there.


Post a reply to this message

From: Darren New
Subject: Re: More random humous
Date: 7 Oct 2008 10:34:55
Message: <48eb738f$1@news.povray.org>
Invisible wrote:
> - I have no idea what Erlang does...

You send a message to another process, or receive such a message, which 
isn't therefore functional as such. Processing the message is (mostly) 
functional, but the behavior of a collection of processes varies with 
timing and such.

Plus, there are processes implemented in C which do non-functional stuff 
with messages, like write them to stdout.

> (A Haskell "program" takes an infinite list of I/O responses as input, and returns
an infinite list of I/O requests as output.

I remember reading about that a long time ago! :-)

-- 
Darren New / San Diego, CA, USA (PST)


Post a reply to this message

From: Stephen
Subject: Re: More random humous
Date: 7 Oct 2008 12:43:12
Message: <ob4ne4t8f15i2t9ouvj71g51okgvtvfbm8@4ax.com>
On Mon, 06 Oct 2008 21:58:46 +0100, Orchid XP v8 <voi### [at] devnull> wrote:

>UNLESS your function uses 
>mutable state and/or performs I/O operations. 

Is that like a WOM (Write Only Memory) :P
-- 

Regards
     Stephen


Post a reply to this message

From: Orchid XP v8
Subject: Re: More random humous
Date: 7 Oct 2008 14:18:44
Message: <48eba804$1@news.povray.org>
>> - I have no idea what Erlang does...
> 
> You send a message to another process, or receive such a message, which 
> isn't therefore functional as such. Processing the message is (mostly) 
> functional, but the behavior of a collection of processes varies with 
> timing and such.
> 
> Plus, there are processes implemented in C which do non-functional stuff 
> with messages, like write them to stdout.

So it's kinda like SQL - it's basically stateless, except for some 
top-level state mutation. (?)

>> (A Haskell "program" takes an infinite list of I/O responses as input, 
>> and returns an infinite list of I/O requests as output.
> 
> I remember reading about that a long time ago! :-)

Yeah. Frighteningly easy to accidentally get the requests and replies 
out of sync. This means it's also very easy to accidentally try to read 
a response before the request has been sent - also known as a 
"deadlock". :-/

We got rid of that For A Reason(tm). ;-)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Darren New
Subject: Re: More random humous
Date: 8 Oct 2008 03:09:03
Message: <48ec5c8f$1@news.povray.org>
Orchid XP v8 wrote:
> So it's kinda like SQL - it's basically stateless, except for some 
> top-level state mutation. (?)

Hmmm. Not really.  I'd say more like, "it isn't functional, it's just 
single-assignment". If you call a function with the same arguments, 
it'll give you the same results, except if it actually looks in its 
message queue. But since the only way to communicate with a process is 
via its message queue, this isn't saying much. Oh, and there are also 
calls to (for example) create a new process or kill off an old process. 
There's also one global mutable hash table per process. So it isn't 
really functional in the sense that the same function necessarily 
produces the same result for a given input. It's more that it's a 
single-assignment language aspiring to be functional but only including 
the annoying parts.  It does have closures, tho.

-- 
Darren New / San Diego, CA, USA (PST)


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 8 Oct 2008 03:48:07
Message: <48ec65b7@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> >> You write a function that takes a PRNG state and returns a random number 
> >> and a new PRNG state.
> > 
> >   Where do you store this state, given that you can't assign it to anything?

> It's not so hard.

  So basically if two completely different functions in completely separate
parts of the program, both being called through long separate chains of
function calls would require using the same RNG stream, you would have to
put the seed parameter to each single function in both chains, even though
most of the functions don't use the RNG for anything themselves.

  If you add calling the RNG to some function in the future, and you want
it to use the same RNG stream, you will then have to add the seed as a
parameter to the function and modify all the lines where that function is
called (which possibly requires to add the seed as parameter to those
functions where those lines are, etc.) If you later remove the need for the
RNG, then the parameter becomes obsolete, but are you going to remove it?

  How do you even create objects which have a state in Haskell? Suppose
that you want to create, for example, sprite objects, each one with their
own state. This state could be, for example, an image (which may be shared
among several sprite objects, or different from other sprite objects, and
which might even change in the future due to some event), their location
on screen and a RNG stream specific to that sprite object. When you call
some update function of the sprite, then the sprite could eg. call the RNG
function using its own RNG stream, and update its own position on screen
based on that. Given that you can't (in theory) assign anything to anything
else, how can you make an object which changes its own state?

  I suppose I just can't stop thinking in the object-oriented way, where
you have objects, each one having its own private state, each one being
able to modify its own state without exposing the details to the outside.
This feels like the ideal design for things like for example sprites, as
described above. I just call, for example, some kind of update function
of those sprites, and they do whatever; I don't know and I don't even
want to know, from the outside, what they are doing inside.

  The Haskell way seems to be that you always have to somehow copy the
elements from one place to another if you want to change them (which in
itself already exposes some of the internal details of the object, as
you need to be aware from the outside that the state of the object is
changed, so you need to copy it somehow). But that seems to make it
impossible to share an object (eg. an image) between several other
objects (eg. sprites) if the state of that first object could be changed.
How do you share a changing object among several other objects?

-- 
                                                          - Warp


Post a reply to this message

From: Invisible
Subject: Re: More random humous
Date: 8 Oct 2008 04:19:43
Message: <48ec6d1f$1@news.povray.org>
Darren New wrote:

> So it isn't really functional. It's more that it's a 
> single-assignment language aspiring to be functional but only including 
> the annoying parts.  It does have closures, tho.

LOL! Nice... I must try it out sometime! ;-)


Post a reply to this message

From: Darren New
Subject: Re: More random humous
Date: 8 Oct 2008 06:19:59
Message: <48ec894f$1@news.povray.org>
Warp wrote:
>   So basically if two completely different functions in completely separate
> parts of the program, both being called through long separate chains of
> function calls would require using the same RNG stream, you would have to
> put the seed parameter to each single function in both chains, even though
> most of the functions don't use the RNG for anything themselves.

In a purely functional language, yes. If the result of the function 
depends on only its arguments, then quite reasonably if the function's 
result depends on the result of the RNG stream, you have to pass the RNG 
stream into the function, even if it's only used at lower levels.

Yes, it's rather a PITA. It's how Erlang works, too, except there's a 
back door where you can stuff some of your values into a per-process 
hash table (where a "process" is a very small and efficient thing in 
Erlang). You have to take care not to walk on someone else's key, tho.

>   If you add calling the RNG to some function in the future, and you want
> it to use the same RNG stream, you will then have to add the seed as a
> parameter to the function and modify all the lines where that function is
> called (which possibly requires to add the seed as parameter to those
> functions where those lines are, etc.) 

Yes. And it shows you that calling that new function may now alter the 
results from other functions that used to take the same stream.

 > If you later remove the need for the
> RNG, then the parameter becomes obsolete, but are you going to remove it?

Recall that all looping is done with recursion, too, so you wind up 
calling yourself a lot more than in other languages, too. So you wind up 
not only having to add it to all the callers, but to all the "loops" as 
well.

>   How do you even create objects which have a state in Haskell? Suppose
> that you want to create, for example, sprite objects, each one with their
> own state. This state could be, for example, an image (which may be shared
> among several sprite objects, or different from other sprite objects, and
> which might even change in the future due to some event), their location
> on screen and a RNG stream specific to that sprite object. When you call
> some update function of the sprite, then the sprite could eg. call the RNG
> function using its own RNG stream, and update its own position on screen
> based on that. Given that you can't (in theory) assign anything to anything
> else, how can you make an object which changes its own state?

You might have a structure called "World" that you pass to a function 
Update.

Function Update(World) ->
    ThisJoysticks = Read_Joysticks()
    NewPlayers = Update_Players(ThisJoysticks, World.Players)
    NewSprites = Update_Sprites(World.Sprites)
    NewWorld = World{NewPlayers, NewSprites}
    Update(NewWorld)

Function Update_Sprites(Sprite_List) ->
    NewSprits = map UpdateOneSprite Sprite_List
function UpdateOneSprite(Sprite) ->
    ThisRNG = Sprite.RNG
    ThisPos = Sprite.Position
    NewRNG = Rand(ThisRNG)
    NewPos = ThisPos + NewRNG.value
    NewSprite = Sprite{Sprite.Image, NewRNG, NewPos}
    Draw(NewSprite.Image, NewSprite.Position)
    return NewSprite

Basically, you track the world, and create a new world from scratch
each time thru the loop by recalculating what all the new parts are
based on the old parts. And you pass up and down everything you need.
It's fairly ugly.

I saw a blog about it that gave a really good explanation, but I can't 
seem to find it any more. I'll keep looking.

> This feels like the ideal design for things like for example sprites, as
> described above.

Not unreasonable. For things like games, where you have a bunch of 
independent actors, an OO approach works well and intuitively.

There's another way to do it, tho, which is to have independent 
functions of time. Some of my stuff worked that way, where the position 
of the sprite is calculated based on the clock, not unlike the way 
POV-Ray handles animation. There the RND was based on the clock also, as 
was the inputs (as in, "return the list of characters he typed up to 
time X").  It took some getting used to, but it was nice because you 
could combine things from different projects and get a nice visual effect.

For example, http://home.san.rr.com/dnew/FootNotes.html is written in 
this (custom) essentially-functional language, and a bunch of the bits 
are reused in lots of places (like, all the random movements are the 
same code, all the ovals are the same code, combining the two gets you 
the alien speaking, ...)  I could probably dig up the sources.

> How do you share a changing object among several other objects?

You put the image as one field of a structure (in C-speak), and the 
position as another field. When you want to move the sprite, you create 
a new sprite with the same image and a new position. The compiler avoids 
copying the actual image because it knows you can't change it so it's OK 
to share it with other sprites.



-- 
Darren New / San Diego, CA, USA (PST)


Post a reply to this message

From: Invisible
Subject: Re: More random humous
Date: 8 Oct 2008 08:56:00
Message: <48ecade0$1@news.povray.org>
Warp wrote:

>   So basically if two completely different functions in completely separate
> parts of the program, both being called through long separate chains of
> function calls would require using the same RNG stream, you would have to
> put the seed parameter to each single function in both chains, even though
> most of the functions don't use the RNG for anything themselves.

Basically... yes.

Presumably it goes without saying that you try to avoid structuring your 
program that way.

>   If you add calling the RNG to some function in the future, and you want
> it to use the same RNG stream, you will then have to add the seed as a
> parameter to the function and modify all the lines where that function is
> called (which possibly requires to add the seed as parameter to those
> functions where those lines are, etc.) If you later remove the need for the
> RNG, then the parameter becomes obsolete, but are you going to remove it?

This kind of refactoring is both common and irritating. There are ways 
around it. For example, if you're doing a lot of work with random 
numbers, or any kind of state-carrying operation, you would probably use 
some kind of monad. That way, changing what gets passed around involves 
changing a few type signatures rather than wholesale editing of the 
whole program. (Especially if those type signatures are automatically 
derived, or even if you just use a few carefully chosen type aliases.)

>   How do you even create objects which have a state in Haskell?

Answer #1: You don't. You structure your program some other way.

Answer #2: If you really want to, Haskell does provide mutable storage.

> Suppose
> that you want to create, for example, sprite objects, each one with their
> own state. This state could be, for example, an image (which may be shared
> among several sprite objects, or different from other sprite objects, and
> which might even change in the future due to some event), their location
> on screen and a RNG stream specific to that sprite object. When you call
> some update function of the sprite, then the sprite could eg. call the RNG
> function using its own RNG stream, and update its own position on screen
> based on that. Given that you can't (in theory) assign anything to anything
> else, how can you make an object which changes its own state?

If the PRNG is unique to each sprite then it's fairly easy. You just 
write some function that takes a sprite structure describing how the 
sprite is right now (i.e., where it is, what image it has, etc.) and 
returns a new sprite structure.

More generally, for something like a complex game with collision 
detection, object interaction, user input from the outside world, etc., 
you would probably have some structure that represents the state of the 
entire "game world", and a function that computes a new state from the 
previous one plus some user input. You then have some functions that 
display this somehow.

>   I suppose I just can't stop thinking in the object-oriented way, where
> you have objects, each one having its own private state, each one being
> able to modify its own state without exposing the details to the outside.
> This feels like the ideal design for things like for example sprites, as
> described above. I just call, for example, some kind of update function
> of those sprites, and they do whatever; I don't know and I don't even
> want to know, from the outside, what they are doing inside.

Where this trips over is if, for example, how one object updates depends 
on the state of another object. Then you have to update them in the 
correct order - and *you* have to know what that order is. Otherwise 
some objects update themselves according to the old state of the world, 
and some according to the new state of the world, and others according 
to a hybrid state that should never actually exist.

If you take the entire game world and produce a new, modified copy of 
it, you know where you are, and you can compute things in any order. The 
only constraint is data dependencies - and the compiler will figure that 
out for you automatically; *you* do not need to care.

>   The Haskell way seems to be that you always have to somehow copy the
> elements from one place to another if you want to change them (which in
> itself already exposes some of the internal details of the object, as
> you need to be aware from the outside that the state of the object is
> changed, so you need to copy it somehow).

Not so much. You ask for the new version of some object, and it gives 
you an object of the appropriate type. Maybe this is a completely new 
object that was just constructed. Maybe it's actually the original 
object, unmodified. You can't tell, and you don't need to care. All that 
matters is that it's *potentially* a different object.

> But that seems to make it
> impossible to share an object (eg. an image) between several other
> objects (eg. sprites) if the state of that first object could be changed.
> How do you share a changing object among several other objects?

You cannot have object A point to object B, and alter object B such that 
object A sees the change. This is by design. You must duplicate object A 
and object B (thus making it clear that something has happened to *both* 
objects) for this to work.

That is, unless you actually use mutable state of course - which there 
is no law against. It just makes things like performing updates in the 
right order more tricky.


Post a reply to this message

From: Warp
Subject: Re: More random humous
Date: 8 Oct 2008 09:02:49
Message: <48ecaf79@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> > How do you share a changing object among several other objects?

> You put the image as one field of a structure (in C-speak), and the 
> position as another field. When you want to move the sprite, you create 
> a new sprite with the same image and a new position. The compiler avoids 
> copying the actual image because it knows you can't change it so it's OK 
> to share it with other sprites.

  What if you want to change the image and want all the sprites using that
image to see the change?

-- 
                                                          - Warp


Post a reply to this message

From: Orchid XP v8
Subject: Re: More random humous
Date: 8 Oct 2008 13:27:18
Message: <48eced76$1@news.povray.org>
Warp wrote:

>   What if you want to change the image and want all the sprites using that
> image to see the change?

In Haskell, you must either manually track down all the sprites and 
update them, or you must explicitly use mutable state.

Notice that this is a *design feature*; it prevents things from suddenly 
changing when you didn't touch them, but only touched something they 
indirectly reference. This behaviour can be unexpected in a large, 
complex system. If you actually _want_ this, you must perform work to 
make it happen. (Or... use mutable state.)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.