简体   繁体   中英

How to update and return a record using an interface in F#

In C#, I am using the following function which I want to move into F#:

public virtual void BringToTop(LayersEnum _TopMostLayer)
        {
            if (LayerName == _TopMostLayer)
            {
                ZIndex = 10;
                Visibility = Visibility.Visible;
            }
            else
            {
                ZIndex = DefaultZIndex;
                Visibility = Visibility.Hidden;
            }
        }

Assuming I have the following F# definitions, how is this implemented in F#:

type DocumentRadioButtons = ChartLayer | WritingLayer | ImageLayer | TranscriptionLayer | PublisherLayer

module WritingLayer=

type Model = 
    {  Name: string
       Visibility: bool 
       DefaultZIndex: int
       ZIndex: int
    }
    interface ILayer with 
         member this.Name with get() = this.Name
         member this.Visibility with get() = this.Visibility
         member this.DefaultZIndex with get() = this.DefaultZIndex
         member this.ZIndex with get() = this.ZIndex

type ProgressNoteWin = {
       ConfirmState: ConfirmState option
       Encounter: view_doctor_encounter
       LastName: string
       DocumentLayer: DocumentRadioButtons
       WritingLayer: WritingLayer.Model
       ImageLayer: ImageLayer.Model
       ChartLayer: ChartLayer.Model
      } 

My very FAILED attempt is:

let BringToTop rb (layer:ILayer) = 
                   
                     if rb.ToString() = layer.Name then {layer with Visibility =true} else {layer with Visibility = false }
                

                { m with WritingLayer = BringToTop documentRadioButton m.WritingLayer},

Error: The record label "Visibility" is not defined

Thank you for any help.

#Addendum:

Ultimately, I would like to implement the interface on the different record types of WritingLayer.Model, ImageLayer.Model, and ChartLayer.Model to be used as:

{m with WritingLayer = BringToTop rb m.WritingLayer; 
  ImageLayer = BringToTop rb m.ImageLayer; 
  ChartLayer = rb m.ChartLayer}

Thank you for your consideration.

This code won't work as-is:

let BringToTop rb (layer:ILayer) = 
    if rb.ToString() = layer.Name then
        { layer with Visibility =true }
    else
        { layer with Visibility = false }

This is because F# doesn't implicitly downcast to a type that implements the interface.

First potential solution:

Switch from being passed an ILayer to just being passed a Model directly. Then your code will work.

Second potential solution:

I'm assuming that since you have an interface, you want to pass that around as the data type rather than the model type itself. If that is the case, then I'd recommend using an Object Expression to update the data like so:

let mkILayer visible (layer: ILayer) =
    { new ILayer with 
         member this.Name = layer.Name
         member this.Visibility = visible
         member this.DefaultZIndex = layer.DefaultZIndex
         member this.ZIndex = layer.ZIndex }
        
        
let BringToTop rb (layer:ILayer) =
    if rb.ToString() = layer.Name then
        mkILayer true layer
    else
        mkILayer false layer

Third potential solution

If you're fine with downcasting , you can shadow your first layer definition like so:

let BringToTop rb (layer:ILayer) = 
    if rb.ToString() = layer.Name then
        let layer = layer :?> Model
        { layer with Visibility =true }
    else
        let layer = layer :?> Model
        { layer with Visibility = false }

But usually whenever a downcast is involved that's a code smell, so I'd really recommend constructing a new instance of the interface instead.

I never could do this the way I wanted to--by using the ILayer interface to update and return the record type it was associated with. I would be most interested in a better way to do this.

What I did end up doing was simply to define three specific functions, one for each type as:

let BringToTopWritingLayer rb layer:WritingLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

                let BringToTopImageLayer rb layer:ImageLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

                let BringToTopChartLayer rb layer:ChartLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

with usage as:

{ m with DocumentLayer = documentRadioButton; 
                      WritingLayer = BringToTopWritingLayer documentRadioButton m.WritingLayer;
                      ImageLayer = BringToTopImageLayer documentRadioButton m.ImageLayer;
                      ChartLayer = BringToTopChartLayer documentRadioButton m.ChartLayer}

This seems highly redundant, but it does seem to compile correctly.

A better answer would be most appreciated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM