|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> - 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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
| |
|
|
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
|
|
| |
| |
|
|
|
|
| |
|
|