Recursive async method looses its context - f#

I am writing a raytracer in F# and I am trying to use multithreading in the montecarlo sampling step.
However, when I run my code with the async variant, the program never returns and runs indefinitely.
My Code Currently:
let rec rayTrace previousTraceDepth ((ray : Ray) , (accEmitted : Color) , (accScatter :Color)) =
if previousTraceDepth > maxTraceDepth
then
accEmitted + backgroundColor*accScatter
else
let newTraceDepth = previousTraceDepth + 1us
let (realSolution,t,surface) = findClosestIntersection ray surfaces
let surfaceGeometry : Hitable = surface.Geometry
if surfaceGeometry.IntersectionAcceptable realSolution t 1.0f (PointForRay ray t)
then
let emittedShading = surface.Emitted
let e = accEmitted + accScatter*emittedShading
let mcSamples = surface.SampleCount
//Synchronous
// let mutable totalShading = e/surface.MCNormalization
// for _ in 1..mcSamples do
// let (doesRayContribute,outRay,cosOfIncidence) = surface.Scatter ray t ((int)newTraceDepth)
// let shading = surface.BRDF*cosOfIncidence / (surface.PDF*surface.MCNormalization)
// let s = accScatter*shading
// totalShading <- totalShading + (rayTrace newTraceDepth (outRay , e , s))
// totalShading
let eMCAdjusted = e / surface.MCNormalization
let shadingSamplesAsync =
[|
for _ in 1..mcSamples -> async {
let (doesRayContribute,outRay,cosOfIncidence) = surface.Scatter ray t ((int)newTraceDepth)
let shading = surface.BRDF*cosOfIncidence / (surface.PDF*surface.MCNormalization)
let s = accScatter*shading
return rayTrace newTraceDepth (outRay,e,s)
}|]
if Array.isEmpty shadingSamplesAsync then
e
else
let shadingSamplesSync = shadingSamplesAsync |> Async.Parallel |> Async.RunSynchronously
Array.sumBy (fun x -> eMCAdjusted + x) shadingSamplesSync
else
accEmitted + backgroundColor*accScatter
How can I make this method async recursive?

I found the cause of the problem:
I was actually calling Async.RunSynchronously from the entry point method i.e rayTraceBase. Apparently nested Async.RunSynchronously is not a good thing. After I removed the second Async.RunSynchronously the code worked.
But it is painfully slow since async contexts are created all over the place and the GC runs non stop.

Related

how can I combine / compose computation expressions, in F#?

This is not for a practical need, but rather to try to learn something.
I am using FSToolKit's asyncResult expression which is very handy and I would like to know if there is a way to 'combine' expressions, such as async and result here, or does a custom expression have to be written?
Here is an example of my function to set the ip to a subdomain, with CloudFlare:
let setSubdomainToIpAsync zoneName url ip =
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
let getZoneAsync (client: CloudFlareClient) =
asyncResult {
let! r = client.Zones.GetAsync()
let! d = decodeResult r
return!
match d |> Seq.filter (fun x -> x.Name = zoneName) |> Seq.toList with
| z::_ -> Ok z // take the first one
| _ -> Error $"zone '{zoneName}' not found"
}
let getRecordsAsync (client: CloudFlareClient) zoneId =
asyncResult {
let! r = client.Zones.DnsRecords.GetAsync(zoneId)
return! decodeResult r
}
let updateRecordAsync (client: CloudFlareClient) zoneId (records: DnsRecord seq) =
asyncResult {
return!
match records |> Seq.filter (fun x -> x.Name = url) |> Seq.toList with
| r::_ -> client.Zones.DnsRecords.UpdateAsync(zoneId, r.Id, ModifiedDnsRecord(Name = url, Content = ip, Type = DnsRecordType.A, Proxied = true))
| [] -> client.Zones.DnsRecords.AddAsync(zoneId, NewDnsRecord(Name = url, Content = ip, Proxied = true))
}
asyncResult {
use client = new CloudFlareClient(Credentials.CloudFlare.Email, Credentials.CloudFlare.Key)
let! zone = getZoneAsync client
let! records = getRecordsAsync client zone.Id
let! update = updateRecordAsync client zone.Id records
return! decodeResult update
}
It is interfacing with a C# lib that handles all the calls to the CloudFlare API and returns a CloudFlareResult object which has a success flag, a result and an error.
I remapped that type to a Result<'a, string> type:
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
And I could write an expression for it (hypothetically since I've been using them but haven't written my own yet), but then I would be happy to have an asyncCloudFlareResult expression, or even an asyncCloudFlareResultOrResult expression, if that makes sense.
I am wondering if there is a mechanism to combine expressions together, the same way FSToolKit does (although I suspect it's just custom code there).
Again, this is a question to learn something, not about the practicality since it would probably add more code than it's worth.
Following Gus' comment, I realized it would be good to illustrate the point with some simpler code:
function DoA : int -> Async<AWSCallResult<int, string>>
function DoB : int -> Async<Result<int, string>>
AWSCallResultAndResult {
let! a = DoA 3
let! b = DoB a
return b
}
in this example I would end up with two types that can take an int and return an error string, but they are different. Both have their expressions so I can chain them as needed.
And the original question is about how these can be combined together.
It's possible to extend CEs with overloads.
The example below makes it possible to use the CustomResult type with a usual result builder.
open FsToolkit.ErrorHandling
type CustomResult<'T, 'TError> =
{ IsError: bool
Error: 'TError
Value: 'T }
type ResultBuilder with
member inline _.Source(result : CustomResult<'T, 'TError>) =
if result.IsError then
Error result.Error
else
Ok result.Value
let computeA () = Ok 42
let computeB () = Ok 23
let computeC () =
{ CustomResult.Error = "oops. This went wrong"
CustomResult.IsError = true
CustomResult.Value = 64 }
let computedResult =
result {
let! a = computeA ()
let! b = computeB ()
let! c = computeC ()
return a + b + c
}

F# Cumulative Returns

I am doing a project and I find myself standing in the following situation:
I am creating a Long-Short portfolio in which I currently have daily returns. My next step would be to calculate the cumulative returns, but I am having trouble trying to do so.
Could anyone help with how to do it?
Thanks in advance.
The code
let long = myFactorPorts.Rows |> Seq.filter(fun row -> row.PortfolioName = "Mine" && row.Index = Some 3)
let short = myFactorPorts.Rows |> Seq.filter(fun row -> row.PortfolioName = "Mine" && row.Index = Some 1)
type Return = { YearMonth : DateTime; Return : float }
let longShort =
let shortMap = short |> Seq.map(fun row -> row.YearMonth, row) |> Map
long
|> Seq.map(fun longObs ->
match Map.tryFind longObs.YearMonth shortMap with
| None -> failwith "probably your date variables are not aligned"
| Some shortObs -> { YearMonth = longObs.YearMonth; Return = longObs.Ret - shortObs.Ret })
|> Seq.toArray
longShort |> Array.take 3
let cumulativereturnslongshort = longShort |> ??
Your question is a bit unclear, because you show us some code related to how you get the data, but you do not say too much about what you want to do and where you are stuck.
However, if I understand it, you have an array of Return values. I'll use a simple example:
type Return = { YearMonth : DateTime; Return : float }
let longShort =
[| { YearMonth = DateTime(2021,1,1); Return = 1.0 }
{ YearMonth = DateTime(2021,2,1); Return = -3.0 }
{ YearMonth = DateTime(2021,3,1); Return = 5.0 }
{ YearMonth = DateTime(2021,4,1); Return = -1.0 } |]
You say you want to understand cummulative returns. I guess this means just summing the previous returns, so the cummulative returns based on the above monthly returns would be 1, -2, 3, 2. If that's the case, you can use Array.scan:
longShort
|> Array.scan (fun prev item ->
{ item with Return = item.Return + prev.Return})
{ YearMonth = DateTime.MinValue; Return = 0.0 }
|> Array.skip 1
The scan function lets you calculate new value for each item in the array, using some state that is passed along. Here, I use the previous Return value as the state (and the function passed to scan just adds the current to the previous return). One trick is that you need some initial value, so I create a Return value with Return = 0.0, but then skip over this to get the result.

Using TaskCompletionSource in F# for use in a .NET library

Considering the following
type MyClass () =
member x.ReadStreamAsync(stream:Stream) =
async {
let tcs = new TaskCompletionSource<int>()
let buffer = Array.create 2048 0uy
let! bytesReadCount = stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
if bytesReadCount > 0 then
for i in 0..bytesReadCount do
if buffer.[i] = 10uy then
tcs.SetResult(i)
// Omitted more code to handle the case if 10uy is not found..
return tcs.Task
}
The code reads from a stream until in meets a certain character (represented by a byte value) at which point the task returned by the method completes.
The function signature of DoSomethingAsync is unit -> Async<Task<int>>, but I would like it to be unit -> Task<int> such that it can be used more generally in .NET.
Can this be done in F# using an asynchronous expression, or do I can to rely more on the Task constructs of .NET?
Given that you don't actually use the async workflow for anything in your example, the easiest solution would be to forgo it entirely:
member x.DoSomethingAsync() =
let tcs = new TaskCompletionSource<int>()
Task.Delay(100).Wait()
tcs.SetResult(10)
tcs.Task
This implementation of DoSomethingAsync has the type unit -> Task<int>.
It's not clear to me exactly what you're trying to do, but why don't you just do the following?
member x.DoSomethingAsync() =
async {
do! Async.Sleep 100
return 10 } |> Async.StartAsTask
This implementation also has the type unit -> Task<int>.
Based on the updated question, here's a way to do it:
member x.DoSomethingAsync(stream:Stream) =
async {
let buffer = Array.create 2048 0uy
let! bytesReadCount =
stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
if bytesReadCount > 0
then
let res =
[0..bytesReadCount]
|> List.tryFind (fun i -> buffer.[i] = 10uy)
return defaultArg res -1
else return -1
}
|> Async.StartAsTask
The DoSomethingAsync function has the type Stream -> System.Task<int>. I didn't know what to do in the else case, so I just put -1, but I'm sure you can replace it with something more correct.

FParsec Reactive Example

I'm hopeful that someone could potentially post an example of using FParsec where the data is based on some sort of incoming live stream.
Some examples could be producing a result based on mouse gestures, generating an alert or notification based on a specific sequence of stock ticks.
If someone could post an example it would be greatly appreciated.
Thanks!
What you're looking for is the Reactive Parsers out of Rxx.
This isn't F# but rather a .NET library that let's you write code such as (following from your stock example):
var alerts = ticks.Parse(parser =>
from next in parser
let ups = next.Where(tick => tick.Change > 0)
let downs = next.Where(tick => tick.Change < 0)
let downAlert = from manyUps in ups.AtLeast(2).ToList()
from reversalDown in downs.NonGreedy()
where reversalDown.Change <= -11
select new StockAlert(manyUps, reversalDown)
let upAlert = from manyDowns in downs.AtLeast(2).ToList()
from reversalUp in ups.NonGreedy()
where reversalUp.Change >= 21
select new StockAlert(manyDowns, reversalUp)
select downAlert.Or(upAlert).Ambiguous(untilCount: 1));
Credit of course goes to Dave Sexton and James Miles who did the majority of this work.
For background reading, the parser extensions to Rxx came out of this discussion: http://qa.social.msdn.microsoft.com/Forums/eu/rx/thread/0f72e5c0-1476-4969-92da-633000346d0d
Here's a very simple example of how this could be used in F#:
open Rxx.Parsers.Reactive
open Rxx.Parsers.Reactive.Linq
// F# shortcuts to Rxx
let where f (a:IObservableParser<_,_>) = a.Where(fun b -> f b)
let toList (parser:IObservableParser<_,_>) = parser.ToList()
let (<&>) (a:IObservableParser<'a,'b>) (b:IObservableParser<'a,'b>) = a.And(b)
let create a =
{ new ObservableParser<_,_>() with
override x.Start = a(x.Next) } :> IObservableParser<_,_>
let parse (parser:IObservableParser<_,_>) (obs:IObservable<_>) = obs.Parse(parser)
// example of grammar
let grammar =
(fun (parser:IObservableParser<_,_>) ->
let next = parser.Next
let bigs = next |> where(fun i -> i > 25)
let smalls = next |> where(fun i -> i <= 25)
bigs <&> smalls |> toList )
|> create
// the test
let random = Random()
let values = Observable.Interval(TimeSpan.FromMilliseconds(500.0)).Select( fun _ -> random.Next(1,50)).Trace().TraceSubscriptions("subbing","subbed","disposing","disposed").Publish()
let sub = values |> parse grammar |> Observable.add(printfn "BIG THEN SMALL: %A")
let test = values.Connect()

Updating nested immutable data structures

I want to update a nested, immutable data structure (I attached a small example of a hypothetical game.) And I wonder whether this can be done a little more elegantly.
Every time something inside the dungeon changes we need a new dungeon. So, I gave it a general update member. The best way to use this, that I could come up with for the general case, is to specify the processing functions for each nesting and than pass the combined function to the update member.
Then, for really common cases (like applying a map to all the monsters on a specific level), I provide extra members (Dungeon.MapMonstersOnLevel).
The whole thing works, I would just like to know, if anyone can think of better ways of doing it.
Thanks!
// types
type Monster(awake : bool) =
member this.Awake = awake
type Room(locked : bool, monsters : Monster list) =
member this.Locked = locked
member this.Monsters = monsters
type Level(illumination : int, rooms : Room list) =
member this.Illumination = illumination
member this.Rooms = rooms
type Dungeon(levels : Level list) =
member this.Levels = levels
member this.Update levelFunc =
new Dungeon(this.Levels |> levelFunc)
member this.MapMonstersOnLevel (f : Monster -> Monster) nLevel =
let monsterFunc = List.map f
let roomFunc = List.map (fun (room : Room) -> new Room(room.Locked, room.Monsters |> monsterFunc))
let levelFunc = List.mapi (fun i (level : Level) -> if i = nLevel then new Level(level.Illumination, level.Rooms |> roomFunc) else level)
new Dungeon(this.Levels |> levelFunc)
member this.Print() =
this.Levels
|> List.iteri (fun i e ->
printfn "Level %d: Illumination %d" i e.Illumination
e.Rooms |> List.iteri (fun i e ->
let state = if e.Locked then "locked" else "unlocked"
printfn " Room %d is %s" i state
e.Monsters |> List.iteri (fun i e ->
let state = if e.Awake then "awake" else "asleep"
printfn " Monster %d is %s" i state)))
// generate test dungeon
let m1 = new Monster(true)
let m2 = new Monster(false)
let m3 = new Monster(true)
let m4 = new Monster(false)
let m5 = new Monster(true)
let m6 = new Monster(false)
let m7 = new Monster(true)
let m8 = new Monster(false)
let r1 = new Room(true, [ m1; m2 ])
let r2 = new Room(false, [ m3; m4 ])
let r3 = new Room(true, [ m5; m6 ])
let r4 = new Room(false, [ m7; m8 ])
let l1 = new Level(100, [ r1; r2 ])
let l2 = new Level(50, [ r3; r4 ])
let dungeon = new Dungeon([ l1; l2 ])
dungeon.Print()
// toggle wake status of all monsters
let dungeon1 = dungeon.MapMonstersOnLevel (fun m -> new Monster(not m.Awake)) 0
dungeon1.Print()
// remove monsters that are asleep which are in locked rooms on levels where illumination < 100 and unlock those rooms
let monsterFunc2 = List.filter (fun (monster : Monster) -> monster.Awake)
let roomFunc2 = List.map(fun (room : Room) -> if room.Locked then new Room(false, room.Monsters |> monsterFunc2) else room)
let levelFunc2 = List.map(fun (level : Level) -> if level.Illumination < 100 then new Level(level.Illumination, level.Rooms |> roomFunc2) else level)
let dungeon2 = dungeon.Update levelFunc2
dungeon2.Print()
Here's the same code using lenses as currently defined in FSharpx.
As other answers note, it's convenient to use records here; they give you structural equality for free among other things.
I also attach the corresponding lenses for the properties as static members; you can also define them in a module or as loose functions. I prefer static members here, for practical purposes it's just like a module.
open FSharpx
type Monster = {
Awake: bool
} with
static member awake =
{ Get = fun (x: Monster) -> x.Awake
Set = fun v (x: Monster) -> { x with Awake = v } }
type Room = {
Locked: bool
Monsters: Monster list
} with
static member locked =
{ Get = fun (x: Room) -> x.Locked
Set = fun v (x: Room) -> { x with Locked = v } }
static member monsters =
{ Get = fun (x: Room) -> x.Monsters
Set = fun v (x: Room) -> { x with Monsters = v } }
type Level = {
Illumination: int
Rooms: Room list
} with
static member illumination =
{ Get = fun (x: Level) -> x.Illumination
Set = fun v (x: Level) -> { x with Illumination = v } }
static member rooms =
{ Get = fun (x: Level) -> x.Rooms
Set = fun v (x: Level) -> { x with Rooms = v } }
type Dungeon = {
Levels: Level list
} with
static member levels =
{ Get = fun (x: Dungeon) -> x.Levels
Set = fun v (x: Dungeon) -> { x with Levels = v } }
static member print (d: Dungeon) =
d.Levels
|> List.iteri (fun i e ->
printfn "Level %d: Illumination %d" i e.Illumination
e.Rooms |> List.iteri (fun i e ->
let state = if e.Locked then "locked" else "unlocked"
printfn " Room %d is %s" i state
e.Monsters |> List.iteri (fun i e ->
let state = if e.Awake then "awake" else "asleep"
printfn " Monster %d is %s" i state)))
I also define print as a static member; again it's like a function in a module, and it's more composable than an instance method (though I won't compose it here).
Now to generate the sample data. I think { Monster.Awake = true } is more desciptive than new Monster(true). If you wanted to use classes I'd name the parameter explicitly, e.g. Monster(awake: true)
// generate test dungeon
let m1 = { Monster.Awake = true }
let m2 = { Monster.Awake = false }
let m3 = { Monster.Awake = true }
let m4 = { Monster.Awake = false }
let m5 = { Monster.Awake = true }
let m6 = { Monster.Awake = false }
let m7 = { Monster.Awake = true }
let m8 = { Monster.Awake = false }
let r1 = { Room.Locked = true; Monsters = [m1; m2] }
let r2 = { Room.Locked = false; Monsters = [m3; m4] }
let r3 = { Room.Locked = true; Monsters = [m5; m6] }
let r4 = { Room.Locked = false; Monsters = [m7; m8] }
let l1 = { Level.Illumination = 100; Rooms = [r1; r2] }
let l2 = { Level.Illumination = 50; Rooms = [r3; r4] }
let dungeon = { Dungeon.Levels = [l1; l2] }
Dungeon.print dungeon
Now comes the fun part: composing lenses to update the monsters for all rooms for a particular level in a dungeon:
open FSharpx.Lens.Operators
let mapMonstersOnLevel nLevel f =
Dungeon.levels >>| Lens.forList nLevel >>| Level.rooms >>| Lens.listMap Room.monsters
|> Lens.update (f |> List.map |> List.map)
// toggle wake status of all monsters
let dungeon1 = dungeon |> mapMonstersOnLevel 0 (Monster.awake.Update not)
Dungeon.print dungeon1
For the second dungeon I also use lenses but without lens composition. It's sort of a DSL defined by small composed functions (some of the functions are from lenses). Maybe there are lenses to express this more concisely, but I haven't figured it out.
// remove monsters that are asleep
// which are in locked rooms on levels where illumination < 100
// and unlock those rooms
let unlock = Room.locked.Set false
let removeAsleepMonsters = Room.monsters.Update (List.filter Monster.awake.Get)
let removeAsleepMonsters_unlock_rooms = List.mapIf Room.locked.Get (unlock >> removeAsleepMonsters)
let isLowIllumination = Level.illumination.Get >> ((>)100)
let removeAsleepMonsters_unlock_level = Level.rooms.Update removeAsleepMonsters_unlock_rooms
let removeAsleepMonsters_unlock_levels = List.mapIf isLowIllumination removeAsleepMonsters_unlock_level
let dungeon2 = dungeon |> Dungeon.levels.Update removeAsleepMonsters_unlock_levels
Dungeon.print dungeon2
I overused lenses and pointfree a bit here, partially on purpose, just to show what it could look like. Some won't like it, claiming it's not idiomatic or clear. Maybe so, but it's another tool that you can choose to use or not, depending on your context.
But more importantly, because Update is a Get followed by a function followed by a Set, this isn't as efficient as your code when it comes to processing lists: an Update in Lens.forList first gets the nth element in the list, which is an O(n) operation.
To summarize:
Pros:
Very concise.
Enables pointfree style.
Code involving lenses is generally oblivious of the source type representation (it can be a class, a record, a single-case DU, a dictionary, it doesn't matter).
Cons:
May be inefficient for some cases in current implementation.
Due to lack of macros, requires some boilerplate.
Thanks for this example, as a result I'll be revising the current design of lenses in FSharpx and see if it can be optimized.
I committed this code to the FSharpx repository: https://github.com/fsharp/fsharpx/commit/136c763e3529abbf91ad52b8127ce11cbb3dff28
I asked a similar question, but about haskell: Is there a Haskell idiom for updating a nested data structure?
The excellent answers mentioned a concept known as functional lenses.
Unfortunately, I don't know what the package is, or if it even exists, for F#.
Update: two knowledgeable F#-ists (F#-ers? F#as?) left useful links about this in comments, so I'll post them here:
#TomasPetricek suggested FSharpX and this website describing it
#RyanRiley gave the link for the package
It's awesome that these two guys took the time to read my answer, comment and improve it, as they're both developers of FSharpX!
More extraneous information: I was motivated to figure out how to do this by Clojure's assoc-in and update-in functions, which proved to me that it is possible in functional languages! Of course, Clojure's dynamic typing makes it simpler than in Haskell/F#. Haskell's solution involves templating, I believe.
I posted a similar question about Scala about a year back. The answers mention three concepts as a solution to this problem: Zippers, Tree rewriting, and Lenses.
I don't know why you want to use classes here. I think you can leverage the power of pattern matching if you use records for holding data and keeping them minimal:
// Types
type Monster = {
Awake: bool
}
with override x.ToString() =
if x.Awake then "awake" else "asleep"
type Room = {
Locked: bool;
Monsters: Monster list
}
with override x.ToString() =
let state = if x.Locked then "locked" else "unlocked"
state + "\n" + (x.Monsters |> List.mapi (fun i m -> sprintf " Monster %d is %s" i (string m)) |> String.concat "\n")
type Level = {
Illumination : int;
Rooms : Room list
}
with override x.ToString() =
(string x.Illumination) + "\n" + (x.Rooms |> List.mapi (fun i r -> sprintf " Room %d is %s" i (string r)) |> String.concat "\n")
type Dungeon = {
Levels: Level list;
}
with override x.ToString() =
x.Levels |> List.mapi (fun i l -> sprintf "Level %d: Illumination %s" i (string l)) |> String.concat "\n"
To me, putting functions for manipulating Dungeon inside the class is unnatural. The code looks better if you put them in a module and make use of above declarations:
/// Utility functions
let updateMonster (m: Monster) a =
{m with Awake = a}
let updateRoom (r: Room) l monstersFunc =
{ Locked = l;
Monsters = r.Monsters |> monstersFunc}
let updateLevel (l: Level) il roomsFunc =
{Illumination = il; Rooms = l.Rooms |> roomsFunc}
let updateDungeon (d: Dungeon) levelsFunc =
{d with Levels = d.Levels |> levelsFunc}
/// Update functions
let mapMonstersOnLevel (d: Dungeon) nLevel =
let monstersFunc = List.map (fun m -> updateMonster m (not m.Awake))
let roomsFunc = List.map (fun r -> updateRoom r r.Locked monstersFunc)
let levelsFunc = List.mapi (fun i l -> if i = nLevel then updateLevel l l.Illumination roomsFunc else l)
updateDungeon d levelsFunc
let removeSleptMonsters (d: Dungeon) =
let monstersFunc = List.filter (fun m -> m.Awake)
let roomsFunc = List.map (fun r -> if r.Locked then updateRoom r false monstersFunc else r)
let levelsFunc = List.map (fun l -> if l.Illumination < 100 then updateLevel l l.Illumination roomsFunc else l)
updateDungeon d levelsFunc
Then you can see manipulating these nested data structures is much easier. However, above functions still have redundancy. You can refactor more if you use lenses which come very natural with records. Check out the insightful article by Mauricio Scheffer, which is really close to this formulation.
I've implemented a lens library in C# via reflection. The core of the library is
this function
/// <summary>
/// Perform an immutable persistent set on a sub
/// property of the object. The object is not
/// mutated rather a copy of the object with
/// the required change is returned.
/// </summary>
/// <typeparam name="ConvertedTo">type of the target object</typeparam>
/// <typeparam name="V">type of the value to be set</typeparam>
/// <param name="This">the target object</param>
/// <param name="names">the list of property names composing the property path</param>
/// <param name="value">the value to assign to the property</param>
/// <returns>A new object with the required change implemented</returns>
private static T Set<T, V>
(this T This, List<string> names, V value)
where T : class, Immutable
{
var name = names.First();
var rest = names.Skip(1).ToList();
if (names.Count == 1)
{
var copy = This.ShallowClone();
copy.SetPrivatePropertyValue(names.First(), value);
return copy as T;
}
else
{
var copy = This.ShallowClone();
var subtree = copy
.GetPrivatePropertyValue<Immutable>(name)
.Set(rest, value);
copy.SetPrivatePropertyValue(names.First(), subtree);
return copy as T;
}
}
The above function is composed using helper library into various utilities,
one of which is an undo stack based on immutable persistent records. There
is an overload of this function
public static Maybe<T> MaybeSet<T,V>
(this T This, Expression<Func<T, V>> prop, V value)
where T : class, Immutable
{
if (!EqualityComparer<V>.Default.Equals(This.Get(prop.Compile()),value))
{
var names = ReactiveUI.Reflection.ExpressionToPropertyNames(prop).ToList();
return This.Set(names, value).ToMaybe();
}
else
{
return None<T>.Default;
}
}
which allows more natural type safe notation using LINQ expressions.
foo = foo.Set(f=>f.A.B.C, 10);
There is a lot of reflection going on in the library but the reduction
in boilerplate is worth the performance hit. See the spec. I only need to
tag the record as Immutable to get it to work. I don't have to
provide getters and settors.
class A : Immutable
{
public int P { get; private set; }
public B B { get; private set; }
public A(int p, B b)
{
P = p;
B = b;
}
}
class B : Immutable
{
public int P { get; private set; }
public C C { get; private set; }
public B(int p, C c)
{
P = p;
C = c;
}
}
class C : Immutable
{
public int P { get; private set; }
public C(int p)
{
P = p;
}
}
namespace Utils.Spec
{
public class ImmutableObjectPatternSpec : IEnableLogger
{
[Fact]
public void SetterSpec()
{
var a = new A
( p:10
, b: new B
( p: 20
, c : new C(30)));
var a_ = a.Set(p => p.B.C.P, 10);
a.Should().NotBe(a_);
a.B.C.P.Should().Be(30);
a_.B.C.P.Should().Be(10);
}
[Fact]
public void StringListGettersShouldWork()
{
var a = new A
( p:10
, b: new B
( p: 20
, c : new C(30)));
var a_ = a.Set(p => p.B.C.P, 10);
a_.Get(p=>p.B.C.P).Should().Be(10);
}
}
}
Perhaps reflection based lenses would reduce boiler plate in F#. Maybe
performance could be improved with caching of the accessors or maybe
IL generation.

Resources