|
List Info
Thread: "ocaml_beginners"::[] RFC: Functional design for a basic simulation system.
|
|
| "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  Portugal |
2007-10-09 02:54:01 |
|
Hello,
I have been looking at how I may go about developing a very simple
simulation system for my experiments. The simulation consists in
generating a set of events (with a given distribution), these events
then cause a state change, the state change is then analyzed and
processed in several steps and finally termination conditions are
checked. When said termination condition is true the simulation ends and
the results are saved.
Now I figured this could be done using composition. For example:
# let ( |> ) f g x = g (f x) ;;
val ( |> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>
# let gen_events s0 = () ;;
val gen_events : 'a -> unit = <fun>
# let gen_states e = () ;;
val gen_states : 'a -> unit = <fun>
# let analyze_data s = () ;;
val analyze_data : 'a -> unit = <fun>
# let check_stop s = () ;;
val check_stop : 'a -> unit = <fun>
Then I could set-up an experiment so:
# let exp = gen_events |> gen_states |> analyze_data |> check_stop ;;
val exp : '_a -> unit = <fun>
And execute it so:
# let start_state = () ;;
val start_state : unit = ()
# let _ = exp start_state ;;
- : unit = ()
Please note that I have used any old types just to get the idea through.
Also note that experiments are set-up with a given set of parameters.
Such parameters can be defined when the "exp" composition is set up. In
addition to this several experiments with the same parameters will be
run consecutively in order to obtain statistical data.
Ok, so far so good. Now notice how the data events and state change are
potentially infinite. I figured the best way to go about it is with lazy
evaluation. In other words we could think of experiments as pipe
wherein the last element "pulls" data in as needed. This pull is chained
throughout the pipe. Note that now the various functions will need to
be evaluated lazily. Lazy evaluation could then be encoded encoded in
various ways
(http://tech.groups.yahoo.com/group/ocaml_beginners/message/5981).
This is all "fine and dandy" but the problem I have however is that the
"gen_events" function depends on the last state reached. In other words
"gen_states" updates the current state which should then be made
available to "gen_events" when the next events are "pulled in". Of
course this could be solved with a global reference but this is not
functional. Alternatively we could combine "gen_events" and "gen_state"
into one function (my best bet?).
My question is: how can one design and implement such a "pull pipe" and
solve the problem I have of propagating state back to the initial
functions. If this is not possible functionally what other options do I
have? Better yet, what is the better way to implement such a system?
TIA,
Hugo F.
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  United States |
2007-10-09 09:43:33 |
|
On Tue, 09 Oct 2007 08:54:01 +0100, Hugo Ferreira wrote
> My question is: how can one design and implement such a "pull pipe" and
> solve the problem I have of propagating state back to the initial
> functions. If this is not possible functionally what other options
> do I have? Better yet, what is the better way to implement such a system?
I've only got a fuzzy idea in my head here of what you're doing, but off the
top of my head you could use the Event module and its channels to pass your
bits of info from one piece of code to the next fellow downstream. Set up
the channel, sync on it, have the producer write to it when it has something
and this flows to the receiver who can then do its part.
--
William D. Neumann
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  United Kingdom |
2007-10-09 10:49:10 |
|
On Tuesday 09 October 2007 08:54:01 Hugo Ferreira wrote:
> # let exp = gen_events |> gen_states |> analyze_data |> check_stop ;;
> val exp : '_a -> unit = <fun>
> ...
> This is all "fine and dandy" but the problem I have however is that the
> "gen_events" function depends on the last state reached. In other words
> "gen_states" updates the current state which should then be made
> available to "gen_events" when the next events are "pulled in". Of
> course this could be solved with a global reference but this is not
> functional. Alternatively we could combine "gen_events" and "gen_state"
> into one function (my best bet?).
Sounds like you want to parameterize gen_events over "exp" and make the
definition recursive with a "let rec":
let rec exp = gen_events exp |> gen_states |> analyze_data |> check_stop
That would be a beautiful solution in Haskell but does not work in OCaml:
# let f x = x;;
val f : 'a -> 'a = <fun>
# let rec x = f x;;
This kind of expression is not allowed as right-hand side of `let rec'
This also doesn't work:
# module rec A : sig val x : int end = struct
let rec x = f A.x
end;;
Cannot safely evaluate the definition of the recursively-defined module A
> My question is: how can one design and implement such a "pull pipe" and
> solve the problem I have of propagating state back to the initial
> functions. If this is not possible functionally what other options do I
> have? Better yet, what is the better way to implement such a system?
I've no idea but I wouldn't ask on the beginners list. I suspect there is a
very elegant solution in OCaml but I cannot think what it is.
--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  Portugal |
2007-10-10 02:12:38 |
|
Hello,
William D. Neumann wrote:
> On Tue, 09 Oct 2007 08:54:01 +0100, Hugo Ferreira wrote
>
>> My question is: how can one design and implement such a "pull pipe" and
>> solve the problem I have of propagating state back to the initial
>> functions. If this is not possible functionally what other options
>> do I have? Better yet, what is the better way to implement such a system?
>
> I've only got a fuzzy idea in my head here of what you're doing,
As usual I seem to go on and on without actually explaining what I
really need/want 8-(. Simply put: the idea is to code a pipe to chain
functions into a computation. I need to easily parametrize and define
the order of these (modular) functions to create and test various
variations of the computation.
> but off the
> top of my head you could use the Event module and its channels to pass your
> bits of info from one piece of code to the next fellow downstream. Set up
> the channel, sync on it, have the producer write to it when it has something
> and this flows to the receiver who can then do its part.
>
Hmmm... What you suggest seems to be heavy weight stuff. I am basically
looking for a good functional programming solution to the problem.
Composition was what I thought of. Maybe CSP or the use of monads (still
don't understand this) may be a solution.
Thanks for the feedback.
Hugo F.
> --
>
> William D. Neumann
>
>
>
> Archives up to November 11, 2006 are also downloadable at http://www.connettivo.net/cntprojects/ocaml_beginners/
> The archives of the very official ocaml list (the seniors' one) can be found at http://caml.inria.fr
> Attachments are banned and you're asked to be polite, avoid flames etc.
> Yahoo! Groups Links
>
>
>
>
__._,_.___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  Portugal |
2007-10-10 02:25:32 |
|
Hello,
Jon Harrop wrote:
> On Tuesday 09 October 2007 08:54:01 Hugo Ferreira wrote:
>> # let exp = gen_events |> gen_states |> analyze_data |> check_stop ;;
>> val exp : '_a -> unit = <fun>
>> ...
>> This is all "fine and dandy" but the problem I have however is that the
>> "gen_events" function depends on the last state reached. In other words
>> "gen_states" updates the current state which should then be made
>> available to "gen_events" when the next events are "pulled in". Of
>> course this could be solved with a global reference but this is not
>> functional. Alternatively we could combine "gen_events" and "gen_state"
>> into one function (my best bet?).
>
> Sounds like you want to parameterize gen_events over "exp" and make the
> definition recursive with a "let rec":
>
Exactly. Of course I could do this with a simple loop but then it would
not be easy to simple add/remove and/or reorder the various functions.
> let rec exp = gen_events exp |> gen_states |> analyze_data |> check_stop
>
> That would be a beautiful solution in Haskell but does not work in OCaml:
>
> # let f x = x;;
> val f : 'a -> 'a = <fun>
> # let rec x = f x;;
> This kind of expression is not allowed as right-hand side of `let rec'
>
> This also doesn't work:
>
> # module rec A : sig val x : int end = struct
> let rec x = f A.x
> end;;
> Cannot safely evaluate the definition of the recursively-defined module A
>
>> My question is: how can one design and implement such a "pull pipe" and
>> solve the problem I have of propagating state back to the initial
>> functions. If this is not possible functionally what other options do I
>> have? Better yet, what is the better way to implement such a system?
>
> I've no idea but I wouldn't ask on the beginners list. I suspect there is a
> very elegant solution in OCaml but I cannot think what it is.
>
I on the other hand suspect that if such a solution does exist it will
be so abstract and details so subtle I won't be able to grasp it 8-(.
Anyway its worth a try to put this question on the other lists.
TIA,
Hugo F.
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  United Kingdom |
2007-10-10 02:39:40 |
|
On Wednesday 10 October 2007 08:25:32 Hugo Ferreira wrote:
> Exactly. Of course I could do this with a simple loop but then it would
> not be easy to simple add/remove and/or reorder the various functions.
Perhaps you just answered your own question. Can you do:
let pipe x = gen_events x |> gen_states |> analyze_data |> check_stop
and then:
let rec recycle f x = recycle f (f x)
recycle pipe x
Escaping via an exception?
--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  Portugal |
2007-10-10 03:27:03 |
|
Hi,
Jon Harrop wrote:
> On Wednesday 10 October 2007 08:25:32 Hugo Ferreira wrote:
>> Exactly. Of course I could do this with a simple loop but then it would
>> not be easy to simple add/remove and/or reorder the various functions.
>
Actually what I meant here was that I could "manually" code the pipe as
a loop wherein I call gen_events and all other functions as often as
needed passing in the last generated state when required.
> Perhaps you just answered your own question. Can you do:
>
> let pipe x = gen_events x |> gen_states |> analyze_data |> check_stop
>
> and then:
>
> let rec recycle f x = recycle f (f x)
> recycle pipe x
>
> Escaping via an exception?
>
Looks feasible but I can't see how I can "pull" the data along the chain
of functions. The above assume that only one even is processed at a
time. gent-states for example will always process 1 events at a time but
analyze_data for example should execute only after a specific sequence
is detected or a fixed number of states have been generated.
Rgds,
HF.
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  United States |
2007-10-10 09:04:40 |
|
On Wed, 10 Oct 2007 08:12:38 +0100, Hugo Ferreira wrote
> Hello,
>
> William D. Neumann wrote:
> > On Tue, 09 Oct 2007 08:54:01 +0100, Hugo Ferreira wrote
> >
> >> My question is: how can one design and implement such a "pull pipe" and
> >> solve the problem I have of propagating state back to the initial
> >> functions. If this is not possible functionally what other options
> >> do I have? Better yet, what is the better way to implement such a
system?
> >
> > I've only got a fuzzy idea in my head here of what you're doing,
>
> As usual I seem to go on and on without actually explaining what I
> really need/want 8-(. Simply put: the idea is to code a pipe to chain
> functions into a computation. I need to easily parametrize and define
> the order of these (modular) functions to create and test various
> variations of the computation.
>
> > but off the
> > top of my head you could use the Event module and its channels to pass
your
> > bits of info from one piece of code to the next fellow downstream. Set
up
> > the channel, sync on it, have the producer write to it when it has
something
> > and this flows to the receiver who can then do its part.
> >
>
> Hmmm... What you suggest seems to be heavy weight stuff. I am basically
> looking for a good functional programming solution to the problem.
> Composition was what I thought of. Maybe CSP or the use of monads (still
> don't understand this) may be a solution.
Not really. Events and channels are pretty easy to use (see chapter 19 of
the O'Reilly book for a basic intro). For example, here's a very cut down
example of your pull model with two proceses. Pracess A takes a seed which
it uses to compute the value it sends to process B, but it doesn't send it
until process B requests it. It's a simple change from here to have process
B send something different from () on the request channel (e.g. something of
type state option) which A can use to create its next value that will go to
B. And from here it shouldn't be hard to hook in a process C that requests
from B, and so on.
(* ------------------------------- *)
open Printf;;
let _ = Random.self_init ();;
let proc_a pull_req data_pipe seed =
printf "Starting proc_an%!";
let ticks = ref 0 in
while true do
let e = Event.receive pull_req in
let () = Event.sync e in
let e2 = Event.send data_pipe (seed + !ticks) in
Event.sync e2;
incr ticks
done;
;;
let proc_b pull_req data_pipe () =
printf "starting proc_bn%!";
let times = Random.int 10 in
for i = 0 to times do
printf "Pulling %d of %d:n%!" i times;
let e' = Event.send pull_req () in
let () = Event.sync e' in
let e2' = Event.receive data_pipe in
let v = Event.sync e2' in
printf "tgot %d, sleeping...n%!" v;
Unix.sleep (Random.int v + 1)
done;
printf "done with proc_bn%!"
;;
let spawn_threads seed =
let pull = Event.new_channel ()
and pipe = Event.new_channel ()
in
let _ = Thread.create (proc_a pull pipe) seed
and tb = Thread.create (proc_b pull pipe) ()
in Thread.join tb
;;
let _ =
spawn_threads (try int_of_string Sys.argv.(1) with _ -> 5);;
(* ------------------------------- *)
I hope this helps.
--
William D. Neumann
__._,_.___
.
__,_._,___
|
| Re: "ocaml_beginners"::[] RFC:
Functional design for a basic simulation
system. |
  Portugal |
2007-10-11 01:49:22 |
|
William,
Thanks for the example. In the "main" mailing list I also got the same
suggestion albeit shown in a different language. I guess threads and
channels are the way to go. Code seems to be clear enough to maintain.
Regards,
Hugo F.
William D. Neumann wrote:
> On Wed, 10 Oct 2007 08:12:38 +0100, Hugo Ferreira wrote
>> Hello,
>>
>> William D. Neumann wrote:
>>> On Tue, 09 Oct 2007 08:54:01 +0100, Hugo Ferreira wrote
>>>
>>>> My question is: how can one design and implement such a "pull pipe" and
>>>> solve the problem I have of propagating state back to the initial
>>>> functions. If this is not possible functionally what other options
>>>> do I have? Better yet, what is the better way to implement such a
> system?
>>> I've only got a fuzzy idea in my head here of what you're doing,
>> As usual I seem to go on and on without actually explaining what I
>> really need/want 8-(. Simply put: the idea is to code a pipe to chain
>> functions into a computation. I need to easily parametrize and define
>> the order of these (modular) functions to create and test various
>> variations of the computation.
>>
>>> but off the
>>> top of my head you could use the Event module and its channels to pass
> your
>>> bits of info from one piece of code to the next fellow downstream. Set
> up
>>> the channel, sync on it, have the producer write to it when it has
> something
>>> and this flows to the receiver who can then do its part.
>>>
>> Hmmm... What you suggest seems to be heavy weight stuff. I am basically
>> looking for a good functional programming solution to the problem.
>> Composition was what I thought of. Maybe CSP or the use of monads (still
>> don't understand this) may be a solution.
>
> Not really. Events and channels are pretty easy to use (see chapter 19 of
> the O'Reilly book for a basic intro). For example, here's a very cut down
> example of your pull model with two proceses. Pracess A takes a seed which
> it uses to compute the value it sends to process B, but it doesn't send it
> until process B requests it. It's a simple change from here to have process
> B send something different from () on the request channel (e.g. something of
> type state option) which A can use to create its next value that will go to
> B. And from here it shouldn't be hard to hook in a process C that requests
> from B, and so on.
>
> (* ------------------------------- *)
> open Printf;;
>
> let _ = Random.self_init ();;
>
> let proc_a pull_req data_pipe seed =
> printf "Starting proc_an%!";
> let ticks = ref 0 in
> while true do
> let e = Event.receive pull_req in
> let () = Event.sync e in
> let e2 = Event.send data_pipe (seed + !ticks) in
> Event.sync e2;
> incr ticks
> done;
> ;;
>
> let proc_b pull_req data_pipe () =
> printf "starting proc_bn%!";
> let times = Random.int 10 in
> for i = 0 to times do
> printf "Pulling %d of %d:n%!" i times;
> let e' = Event.send pull_req () in
> let () = Event.sync e' in
> let e2' = Event.receive data_pipe in
> let v = Event.sync e2' in
> printf "tgot %d, sleeping...n%!" v;
> Unix.sleep (Random.int v + 1)
> done;
> printf "done with proc_bn%!"
> ;;
>
> let spawn_threads seed =
> let pull = Event.new_channel ()
> and pipe = Event.new_channel ()
> in
> let _ = Thread.create (proc_a pull pipe) seed
> and tb = Thread.create (proc_b pull pipe) ()
> in Thread.join tb
> ;;
>
> let _ =
> spawn_threads (try int_of_string Sys.argv.(1) with _ -> 5);;
> (* ------------------------------- *)
>
> I hope this helps.
>
> --
>
> William D. Neumann
>
>
>
> Archives up to November 11, 2006 are also downloadable at http://www.connettivo.net/cntprojects/ocaml_beginners/
> The archives of the very official ocaml list (the seniors' one) can be found at http://caml.inria.fr
> Attachments are banned and you're asked to be polite, avoid flames etc.
> Yahoo! Groups Links
>
>
>
>
__._,_.___
|
[1-9]
|
|