Reputation: 9934
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
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