I'm wanting to write a big chunk of C# code using immutable F#. It's a device monitor, and the current implementation works by constantly getting data from a serial port and updating member variables based on new data. I'd like to transfer that to F# and get the benefits of immutable records, but my first shot at a proof-of-concept implementation is really slow.
open System
open System.Diagnostics
type DeviceStatus = { RPM : int;
Pressure : int;
Temperature : int }
// I'm assuming my actual implementation, using serial data, would be something like
// "let rec UpdateStatusWithSerialReadings (status:DeviceStatus) (serialInput:string[])".
// where serialInput is whatever the device streamed out since the previous check: something like
// ["RPM=90","Pres=50","Temp=85","RPM=40","Pres=23", etc.]
// The device streams out different parameters at different intervals, so I can't just wait for them all to arrive and aggregate them all at once.
// I'm just doing a POC here, so want to eliminate noise from parsing etc.
// So this just updates the status's RPM i times and returns the result.
let rec UpdateStatusITimes (status:DeviceStatus) (i:int) =
match i with
| 0 -> status
| _ -> UpdateStatusITimes {status with RPM = 90} (i - 1)
let initStatus = { RPM = 80 ; Pressure = 100 ; Temperature = 70 }
let stopwatch = new Stopwatch()
stopwatch.Start()
let endStatus = UpdateStatusITimes initStatus 100000000
stopwatch.Stop()
printfn "endStatus.RPM = %A" endStatus.RPM
printfn "stopwatch.ElapsedMilliseconds = %A" stopwatch.ElapsedMilliseconds
Console.ReadLine() |> ignore
This runs in about 1400 ms on my machine, whereas the equivalent C# code (with mutable member variables) runs in around 310 ms. Is there any way to speed this up without losing the immutability? I was hoping that the F# compiler would notice that initStatus and all the intermediate status variables were never reused, and thus simply mutate those records behind the scene, but I guess not.
All you have to do is sub-class from Freezer . After initialization of the Python, I “freeze” the object with my overriding of the __delattr__ and __setattr__ methods on the object. I set _frozen = True which indicates the instance is now “frozen”. objects become immutable.
Immutable class in java means that once an object is created, we cannot change its content. In Java, all the wrapper classes (like Integer, Boolean, Byte, Short) and String class is immutable. We can create our own immutable class as well.
Immutable class means once the object of the class is created its fields cannot be modified or changed. In Java, all the wrapper classes like Boolean, Short, Integer, Long, Float, Double, Byte, Char, and String classes are immutable classes.
In the F# community, imperative code and mutable data aren't frowned upon as long as they're not part of your public interface. I.e., using mutable data is fine as long as you encapsulate it and isolate it from the rest of your code. To that end, I suggest something like:
type DeviceStatus =
{ RPM : int
Pressure : int
Temperature : int }
// one of the rare scenarios in which I prefer explicit classes,
// to avoid writing out all the get/set properties for each field
[<Sealed>]
type private DeviceStatusFacade =
val mutable RPM : int
val mutable Pressure : int
val mutable Temperature : int
new(s) =
{ RPM = s.RPM; Pressure = s.Pressure; Temperature = s.Temperature }
member x.ToDeviceStatus () =
{ RPM = x.RPM; Pressure = x.Pressure; Temperature = x.Temperature }
let UpdateStatusITimes status i =
let facade = DeviceStatusFacade(status)
let rec impl i =
if i > 0 then
facade.RPM <- 90
impl (i - 1)
impl i
facade.ToDeviceStatus ()
let initStatus = { RPM = 80; Pressure = 100; Temperature = 70 }
let stopwatch = System.Diagnostics.Stopwatch.StartNew ()
let endStatus = UpdateStatusITimes initStatus 100000000
stopwatch.Stop ()
printfn "endStatus.RPM = %d" endStatus.RPM
printfn "stopwatch.ElapsedMilliseconds = %d" stopwatch.ElapsedMilliseconds
stdin.ReadLine () |> ignore
This way, the public interface is unaffected – UpdateStatusITimes
still takes and returns an intrinsically immutable DeviceStatus
– but internally UpdateStatusITimes
uses a mutable class to eliminate allocation overhead.
EDIT: (In response to comment) This is the style of class I would normally prefer, using a primary constructor and let
s + properties rather than val
s:
[<Sealed>]
type private DeviceStatusFacade(status) =
let mutable rpm = status.RPM
let mutable pressure = status.Pressure
let mutable temp = status.Temperature
member x.RPM with get () = rpm and set n = rpm <- n
member x.Pressure with get () = pressure and set n = pressure <- n
member x.Temperature with get () = temp and set n = temp <- n
member x.ToDeviceStatus () =
{ RPM = rpm; Pressure = pressure; Temperature = temp }
But for simple facade classes where each property will be a blind getter/setter, I find this a bit tedious.
F# 3+ allows for the following instead, but I still don't find it to be an improvement, personally (unless one dogmatically avoids fields):
[<Sealed>]
type private DeviceStatusFacade(status) =
member val RPM = status.RPM with get, set
member val Pressure = status.Pressure with get, set
member val Temperature = status.Temperature with get, set
member x.ToDeviceStatus () =
{ RPM = x.RPM; Pressure = x.Pressure; Temperature = x.Temperature }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With