LAgent: an agent framework in F# – Part VI – Hot swapping of code (and something silly)

-

Download frame­work here.

All posts are here:

Hot swapping of code

Let’s get back a cou­ple of steps and con­sider what hap­pens when you get an er­ror. Sure, your agent will con­tinue pro­cess­ing mes­sages, but it might be do­ing the wrong thing. Your mes­sage han­dling code might be buggy.

Ideally you’d want to patch things on the fly. You’d want to re­place the mes­sage pro­cess­ing code for an agent with­out stop­ping it.

Here is how you do it:

let counter2 = spawnAgent (fun msg state -> printfn "From %i to %i" state (state + msg);
state + msg) 0 counter2 <-- 2 counter2 <-- SetAgentHandler(fun msg state –>
printfn "From %i to %i via multiplication" state (state * msg); msg * state) counter2 <-- 3

Which gen­er­ates:

**From 0 to 2

From 2 to 6 via mul­ti­pli­ca­tion**

After the agent re­ceives a SetAgentHandler mes­sage, it switch from a +’ agent to a *’ agent on the fly!! All the mes­sages that come af­ter that one gets mul­ti­plied to the state. Also, the state is pre­served be­tween changes in be­hav­ior.

It might not be im­me­di­ately ap­par­ent how to load a func­tion at run­time, but it is re­ally sim­ple. Imagine that I get the data on the func­tion to load from some­where (i.e. a man­age­ment con­sole UI).

let assemblyNameFromSomewhere, typeNameFromSomewhere, methodNameFromSomewhere = 
"mscorlib.dll", "System.Console", "WriteLine"

I can then use it to dy­nam­i­cally load a mes­sage han­dler (in this case Console.Writeline).

let a = Assembly.Load(assemblyNameFromSomewhere)
let c = a.GetType(typeNameFromSomewhere)
let m = c.GetMethod(methodNameFromSomewhere, [|"".GetType()|])
let newF = fun (msg:string) (state:obj) -> m.Invoke(null, [| (msg:>obj) |])

And then it is as sim­ple as post­ing a SetAgentHandler.

counter2 <-- SetAgentHandler(newF)
counter2 <-- "blah"

Now our coun­ter2 agent has be­come an echo agent on the fly, hav­ing loaded Console.WriteLine dy­nam­i­cally. Note how the agent moved from be­ing a +’ agent tak­ing in­te­gers to be­ing a *’ agent tak­ing in­te­gers to be­ing an echo’ agent tak­ing strings. And it did­n’t stop pro­cess­ing mes­sages for the whole time.

Obviously, you can do the same thing with work­ers:

echo <-- SetWorkerHandler(fun msg -> printfn "I'm an echo and I say: %s" msg)
echo <-- "Hello"

And par­al­lel­Work­ers:

parallelEcho <-- SetWorkerHandler(fun msg -> tprint ("I'm new and " + msg))
messages |> Seq.iter (fun msg -> parallelEcho <-- msg)

A silly interlude

As a way to show some agents talk­ing to each other, here is a sim­ple pro­gram that sim­u­lates mar­i­tal in­ter­ac­tions (of the worst kind):

let rec husband = spawnWorker (fun (To, msg) -> printfn "Husband says: %s" msg; To <-- msg)
let rec wife = spawnWorker (fun msg -> printfn "Wife says: screw you and your '%s'" msg)
husband <-- (wife, "Hello")
husband <-- (wife, "But darling ...")
husband <-- (wife, "ok")

Which pro­duces:

**Husband says: Hello

Husband says: But dar­ling

Wife says: screw you and your Hello’

Wife says: screw you and your But dar­ling’

Husband says: ok

Wife says: screw you and your ok’**

And yes, you can­not ex­pect mes­sages to be in the right se­quence … Next up is an auc­tion ap­pli­ca­tion.

Tags

1 Comment

Comments

Gustavo Keener

2009-07-04T11:18:01Z

ROTFL.  I love it.  Going to Tweet as Wife’s don’t make your hus­bands think this, lol”!  Thanks, Luca.  –Gus