Discrimination union & let binding?

Why are forbidden bindings prohibited in a discriminatory union? I assume this is because the bindings are done in the default constructor?

On a secondary note, any suggestions on how I could rewrite AI_Choose will be appreciated. I want to keep weighted priority in the tuple with AI. My idea is to AI_Weighted_Priority inherit AI_Priority and override Select. I don't want to deal with list entries of different lengths (bad practice imo.)

 open AI type Condition = | Closest of float | Min | Max | Average member this.Select (aiListWeight : list<AI * float>) = match this with | Closest(x) -> aiListWeight |> List.minBy (fun (ai, priority) -> abs(x - priority)) | Min -> aiListWeight |> List.minBy snd | Max -> aiListWeight |> List.maxBy snd | Average -> let average = aiListWeight |> List.averageBy snd aiListWeight |> List.minBy (fun (ai, priority) -> abs(average - priority)) type AI_Choose = | AI_Priority of list<AI> * Condition | AI_Weighted_Priority of list<AI * float> * Condition // I'm sad that I can't do this let mutable chosen = Option<AI>.None member this.Choose() = match this with | AI_Priority(aiList, condition) -> aiList |> List.map (fun ai -> ai, ai.Priority()) |> condition.Select |> fst | AI_Weighted_Priority(aiList, condition) -> aiList |> List.map (fun (ai, weight) -> ai, weight * ai.Priority()) |> condition.Select |> fst member this.Chosen with get() = if Option.isNone chosen then chosen <- Some(this.Choose()) chosen.Value and set(x) = if Option.isSome chosen then chosen.Value.Stop() chosen <- Some(x) x.Start() interface AI with member this.Start() = this.Chosen.Start() member this.Stop() = this.Chosen.Stop() member this.Reset() = this.Chosen <- this.Choose() member this.Priority() = this.Chosen.Priority() member this.Update(gameTime) = this.Chosen.Update(gameTime) 
+6
f # discriminated-union
source share
3 answers

it would be prudent to allow β€œobstructing” to bind within discriminated unions. I think the reason this is not possible is because discriminatory unions are still based on the OCaml design, while objects come from the .NET world. F # is trying to integrate the two as much as possible, but perhaps this can go further.

In any case, it seems to me that you use the discriminatory union only to implement some internal behavior like AI_Choose . In this case, you can declare the discriminated union separately and use it to implement the type of object.

I think you could write something like this:

 type AiChooseOptions = | AI_Priority of list<AI> * Condition | AI_Weighted_Priority of list<AI * float> * Condition type AiChoose(aiOptions) = let mutable chosen = Option<AI>.None member this.Choose() = match aiOptions with | AI_Priority(aiList, condition) -> (...) | AI_Weighted_Priority(aiList, condition) -> (...) member this.Chosen (...) interface AI with (...) 

The key difference between class hierarchies and discriminatory associations is when it comes to extensibility. Classes make it easy to add new types, while discriminatory associations make it easy to add new functions that work with the type (in your case, AiChooseOptions), so this is probably the first thing to consider when developing an application.

+2
source share

For anyone interested, I got the output of AI_Priority and AI_Weighted_Priority from an abstract base class.

 [<AbstractClass>] type AI_Choose() = let mutable chosen = Option<AI>.None abstract member Choose : unit -> AI member this.Chosen with get() = if Option.isNone chosen then chosen <- Some(this.Choose()) chosen.Value and set(x) = if Option.isSome chosen then chosen.Value.Stop() chosen <- Some(x) x.Start() interface AI with member this.Start() = this.Chosen.Start() member this.Stop() = this.Chosen.Stop() member this.Reset() = this.Chosen <- this.Choose() member this.Priority() = this.Chosen.Priority() member this.Update(gameTime) = this.Chosen.Update(gameTime) type AI_Priority(aiList : list<AI>, condition : Condition) = inherit AI_Choose() override this.Choose() = aiList |> List.map (fun ai -> ai, ai.Priority()) |> condition.Select |> fst type AI_Weighted_Priority(aiList : list<AI * float>, condition : Condition) = inherit AI_Choose() override this.Choose() = aiList |> List.map (fun (ai, weight) -> ai, weight * ai.Priority()) |> condition.Select |> fst 
+3
source share

Repeating this code, I ended up with a Tomas sentence that turned out to be much cleaner.

 type AiChooseOptions = | Priority of List<AI * Priority> | WeightedPriority of List<AI * Priority * float> member this.Choose(condition : Condition) = match this with | Priority(list) -> list |> List.map (fun (ai, priority) -> ai, priority.Priority()) |> condition.Select | WeightedPriority(list) -> list |> List.map (fun (ai, p, weight) -> ai, p.Priority() * weight) |> condition.Select type AiChoose(condition, list : AiChooseOptions ) = let mutable chosen = Unchecked.defaultof<AI>, 0.0 interface AI with member this.Update(gameTime) = (fst chosen).Update(gameTime) interface Priority with member this.Priority() = chosen <- list.Choose(condition) (snd chosen) 
+3
source share

All Articles