Variable type escapes scope when combining a generic and not a generic class

I have a generic class in F # with one type of parameter and would like to create a static class containing factory methods. When I write my classes, the F # compiler generates an error related to "a type variable that implements its scope". My question is why there is an error and how to fix it.

I created a minimal size fragment demonstrating the problem:

type Foo<'a>(element : 'a) = member this.Copy () = Bar.Create(element) and Bar = static member Create(element : 'a) = new Foo<'a>(element) 

There is mutual recursion in types, because I would like the type Foo<'a> be able to call factory methods in a static class. The above snippet does not compile, and an error occurs: "Type inference forced a variable of type a to leave the scope. Consider adding an explicit declaration of the type parameter or changing the code to a more general one." The error is registered as being in the Create method of the Bar class. Unfortunately, I really do not understand the problem and how to fix it. Any ideas?

Here's an extra observation. Fragment

 type Foo<'a>(element : 'a) = member this.Element = element and Bar = static member Create(element : 'a) = new Foo<'a>(element) 

does a compilation. Thus, the problem is apparently related to type inference based on the Copy() method of class Foo<'a> . Also fragment

 type Foo<'a>(element : 'a) = member this.Copy () = Bar.Create(element) and Bar = static member Create<'a>(element) = new Foo<'a>(element) 

- this is a more C # -like version of the code (where the static method is explicitly generalized), which also does not compile with the error "This code is not general enough. A variable of type a cannot be generalized because it leaves the scope."

+6
source share
3 answers

Type inference for recursive members often requires type annotations for at least some definitions. However, sometimes you can avoid this by overriding the definitions, as you can, at least in your simplified playback:

 type Bar = static member Create(element) = Foo(element) and Foo<'a>(element:'a) = member this.Copy() = Bar.Create(element) 

(note that I even deleted the annotation on element in Bar.Create ).

I do not know that there is an easy-to-understand explanation of what annotations are required in any particular situation, unfortunately.

+6
source

This seems to work without creating a Bar generic:

 type Foo<'a>(element : 'a) = member this.Copy () = Bar.Create element and Bar = static member Create<'a>(element : 'a) : Foo<'a> = Foo(element) 

Online demo

I don’t know why, just found through the trial version and error.

+4
source

Actually, I see another error stating that a variable of type 'a not resolved, and I can get around it by parameterizing Bar with 'a :

 type Foo<'a>(element : 'a) = member this.Copy () = Bar.Create(element) and Bar<'a> = static member Create(element : 'a) = new Foo<'a>(element) 

I'm afraid I don’t have a very good explanation of why this is required in a scenario where you have mutually recursive types, and not when you have a separate Bar type.

I try to avoid mutually recursive types - situations in which you cannot do without them are rare. Most of the time, you can restructure the code to avoid recursion, and as a rule, you end up with something easier to read and refactor if necessary.

+2
source

Source: https://habr.com/ru/post/1014230/


All Articles