Search code examples
.netf#blazorbolero

F# Code is not sufficiently generic (using a static member constraint)


I'm trying to create a generic function that checks whether a record is in a valid format, given the record implements the static member valid. When trying to use this in an ElmishComponent within the Bolero (Blazor) framework, I'm getting the following error

This code is not sufficiently generic. The type variable ^childModel when ^childModel : (static member valid : ^childModel -> bool) could not be generalized because it would escape its scope

With the following code

module Modal =
    type Message<'childModel, 'childMessage> = | Confirm of 'childModel | Cancel | ChildMessage of 'childMessage
    type Model<'T> = { Display : bool; Title : string; Child : 'T }
    let inline valid (x: ^t) =
        (^t: (static member valid: ^t -> bool) (x))

    type Component<'T, ^childModel, 'childMessage when 'T :> ElmishComponent< ^childModel, 'childMessage> and ^childModel : (static member valid: ^childModel -> bool)>() =
        inherit ElmishComponent<Model<'childModel>, Message<'childModel, 'childMessage>>()

        // Error is highlighted on this line
        override this.View model dispatch = 
            cond model.Display <| function
            | true ->
                div [ attr.style (if model.Display then "background: lightblue;" else "background: grey;") ] [
                    h3 [] [ text model.Title ]
                    ecomp<'T,_,_> model.Child (dispatch << ChildMessage)
                    p [] []
                    button [ 
                        // This is where I use the valid function
                        attr.disabled (if valid model.Child then null else "true")
                        on.click (fun _ -> dispatch <| Confirm model.Child)
                    ] [ text "Confirm" ]
                    button [ on.click (fun _ -> dispatch Cancel) ] [ text "Cancel" ]
                ]
            | false ->
                empty

Solution

  • I may be missing something, but it seems to me that a simpler approach would be to use an interface that the child model implements - then you do not have to bother with static member constraints at all:

    type IValidable =
      abstract IsValid : bool
    
    type Component<'T, 'childModel, 'childMessage when 
          'T :> ElmishComponent< 'childModel, 'childMessage> and 
          'childModel :> IValidable>() =
        inherit ElmishComponent<Model<'childModel>, Message<'childModel, 'childMessage>>()
        override this.View model dispatch = 
            let test = model.Child.IsValid            
            ()