robkuz
robkuz

Reputation: 9934

How can I get rid of this "Can not be generalised" error?

I have this interface declarations

type IModel<'value, 'search, 'target when 'target :> IModel<'value, 'search, 'target>> =
    abstract token: string with get
    abstract value: 'value with get
    abstract search: 'search with get
    abstract GetEmpty: unit -> 'target
    abstract ReInitWith:  #IModel<_, 'search, _> -> 'target

type IModelSimple<'value, 'search> =
    inherit IModel<'value, 'search, IModelSimple<'value, 'search>>
    abstract Update:  ?token:string * ?value: 'value * ?search: 'search -> IModelSimple<'value, 'search>

and this function that creates a object expression

let rec mkModelSimple<'value, 'search> vctor sctor token value search =
    {
        new IModelSimple<'value, 'search> with
            member this.token = token
            member this.value = value
            member this.search = search
            member this.GetEmpty() = mkModelSimple vctor sctor token (vctor()) (sctor())
            member this.ReInitWith (m: #IModel<_, 'search, _>) = mkModelSimple vctor sctor m.token this.value m.search
            member this.Update(?t:Token, ?v: 'value, ?s: 'search) =
                mkModelSimple vctor sctor (defaultArg t this.token) (defaultArg v this.value) (defaultArg s this.search)
    }

This works fine.

Now I want to remodel the above types into

type IModel<'value, 'target when 'target :> IModel<'value, 'target>> =
    abstract token: string with get
    abstract value: 'value with get
    abstract GetEmpty: unit -> 'target

type ISearchModel<'value, 'search, 'target when 'target :> ISearchModel<'value, 'search, 'target>> =
    inherit IModel<'value, 'target>
    abstract search: 'search with get
    abstract ReInitWith:  ISearchModel<_, _, _> -> 'target

type ISearchModelSimple<'value, 'search> =
    inherit ISearchModel<'value, 'search, ISearchModelSimple<'value, 'search>>
    abstract Update:  ?token:string * ?value: 'value * ?search: 'search -> ISearchModelSimple<'value, 'search>

Almost the same as above only that the "search aspect" has been extracted

Now I when implementing the function to create the object expression

let rec mkSearchModelSimple<'value, 'search> vctor sctor token value search =
    {
        new ISearchModelSimple<'value, 'search> with
            member this.token = token
            member this.value = value
            member this.search = search
            member this.GetEmpty() = mkSearchModelSimple vctor sctor token (vctor()) (sctor())
            member this.ReInitWith (m: #ISearchModel<_, 'search, _>) = mkSearchModelSimple vctor sctor m.token this.value m.search
            member this.Update(?t:Token, ?v: 'value, ?s: 'search) =
                mkSearchModelSimple vctor sctor (defaultArg t this.token) (defaultArg v this.value) (defaultArg s this.search)
    }

I get the infamous This code is not sufficiently generic. The type variable 'a could not be generalized because it would escape its scope. on the method ReInitWith This drives me crazy. For one I don't understand why this seemingly and otherwise straight forward change creates an error at all and on the other hand what is the error message trying to communicate?

Upvotes: 3

Views: 130

Answers (1)

Taylor Wood
Taylor Wood

Reputation: 16194

One problem with your second example is this ISearchModel method with all unbound generic type arguments:

abstract ReInitWith: ISearchModel<_, _, _> -> 'target

In your first example, the second generic type is bound to the containing interface's 'search type:

abstract ReInitWith:  #IModel<_, 'search, _> -> 'target

If you remove that type constraint from your first example, it fails to compile in the exact same way.

Your second example works if you constrain the second generic type as you have in your first example:

type ISearchModel<'value, 'search, 'target when 'target :> ISearchModel<'value, 'search, 'target>> =
    inherit IModel<'value, 'target>
    abstract search: 'search with get
    abstract ReInitWith: ISearchModel<_, 'search, _> -> 'target

This produces the following type signature for mkSearchModelSimple:

val mkSearchModelSimple :
  vctor:(unit -> 'value) ->
    sctor:(unit -> 'search) ->
      token:string ->
        value:'value -> search:'search -> ISearchModelSimple<'value,'search>

Note: I replaced your Token type reference with string; its definition wasn't provided.

Upvotes: 5

Related Questions