2

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
Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
Weebs
  • 435
  • 6
  • 17

1 Answers1

2

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            
        ()
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Correct, this is what I ended up going with. AFAIK though I can't implement the IValidable interface on an existing type (ex: a third party component), which I can do with static methods. Although I suppose I could always create a new type that inherits from the 3rd party component and implement the interface on that – Weebs Jan 17 '19 at 17:17
  • As some additional context I was trying to emulate type classes like this article: https://withouttheloop.com/articles/2014-10-21-fsharp-adhoc-polymorphism/ – Weebs Jan 17 '19 at 17:54