I am currently working in a game and I use Event / Observables a lot, one thing I came across was to eliminate some redundant code, and I did not find a way to do this. To explain this, suppose we have the following DUs and the DUs observed for this.
type Health = | Healed | Damaged | Died | Revived let health = Event<Health>() let pub = health.Publish
I have many such structures. Having all the Health messages grouped together is useful and necessary in some situations, but in some situations I only deal with a special message. Since this is still often necessary, I use Observable.choose to separate these messages. I then have code like this.
let healed = pub |> Observable.choose (function | Healed -> Some () | _ -> None ) let damaged = pub |> Observable.choose (function | Damaged -> Some () | _ -> None )
Writing such code is actually quite repetitive and annoying. I have many of these types and messages. Thus, one “rule” of functional programming is “Parametrize all the things”. So I wrote an only function to just help me.
let only msg pub = pub |> Observable.choose (function | x when x = msg -> Some () | _ -> None )
With this feature in place, the code now becomes much shorter and less annoying to write.
let healed = pub |> only Healed let damaged = pub |> only Damaged let died = pub |> only Died let revived = pub |> only Revived
EDIT: It is important to note. healed , damaged , died , revived now have the type IObservable<unit> not IObservable<Health> . The idea is not only to separate messages. This can be easily achieved with Observable.filter . The idea is that data for each case is additionally retrieved. For a DU case that does not carry any additional data, this is easy, since I need to write Some () in the Observable.choose function.
But this only works until different cases in the DU expect additional values. Unlucky, I also have many cases that contain additional information. For example, instead of healed or damaged , I have HealedBy of int . Thus, the message also contains additional information about how much something was healed. In this case, I am doing something like this.
let healedBy = pub |> Observable.choose (function | HealedBy x -> Some x | _ -> None )
But I really want to write something like this
let healedBy = pub |> onlyWith HealeadBy
I expect to get an Observable<int> . And I did not find a way to do this. I can not write a function like only above. because when I try to evaluate msg inside the pattern match, it is just treated as a variable for Pattern Match all cases. I can’t say something like: "Case coincidence inside a variable."
I can check if a variable has any particular case. I can do if x = HealedBy then , but after that I cannot extract any data from x . What I really need is something like an “insecure” option, for example, provide it with optional.Value . Is there a way to implement such a "onlyWith" function to delete a template?
EDIT: The idea is not only to separate the various messages. This can be achieved using Observable.filter . Here healedBy is of type IObservable<int> NOT IObservable<Health> . The big idea is to separate the AND messages and the data distractions that it conducts along the AND without a special pattern. I can already split and extract it at a time using Observable.choose . As long as the case has no additional data, I can use the only function to get rid of the template.
But as soon as the case has additional data, I will return to writing the repeating Observable.choose function and repeat all the pattern matching. I currently have such code.
let observ = pub |> Observable.choose (function | X (a) -> Some a | _ -> None )
And I have such things for a lot of posts and different types. But the only thing that changes is the “X” in it. So I obviously want to parameterize "X", so I don’t need to write the whole construct over and over again. At best, it should be
let observ = anyObservable |> onlyWith CaseIWantToSeparate
But the new Observable refers to the type of specific case that I shared. Not the type of DU itself.