Consider this DU:
type Foo = | Foo
My understanding was that this is equivalent:
type Foo = Foo
However, if we introduce generics they are not:
// Does not compile
type Bar<'t> = Bar
// Compiles
type Bar<'t> = | Bar
What's going on here?
More discoveries:
// Works
type Bar<'t> = Bar of 't
// Works
type Bar<'t> = | Bar of 't
I think there are two interesting cases here. The first one is:
type Foo = Foo
This looks like a self-referential type alias at first, but that's not allowed, so the compiler instead accepts it as a valid DU. This is correct, although subtle, behavior.
The second interesting case is:
type Bar<'t> = Bar // Error: The type 'Bar<_>' expects 1 type argument(s) but is given 0
This also looks like a self-referential type alias at first, but with the wrong number of type parameters. Since the reference is invalid, the compiler issues an error before it has a chance to realize that it's actually looking at a valid DU definition. I think one could reasonably argue that this is a bug in the compiler, and I suggest submitting it as an issue to the F# compiler team. The expected behavior is that this is a valid DU, just like type Foo = Foo and type Bar<'t> = | Bar.
Note also that the following is (correctly) not allowed:
type Bar<'t> = Bar<'t> // Error: This type definition involves an immediate cyclic reference through an abbreviation
The compiler source-code has the following to say:
// This unfortunate case deals with "type x = A"
// In F# this only defines a new type if A is not in scope
// as a type constructor, or if the form type A = A is used.
// "type x = | A" can always be used instead.
Related
In the following code I define two interfaces, the second of which takes the first as a type parameter. However the code gives the error "type parameter 'a' is not defined".
type IFirst<'a> =
abstract Data : 'a
type ISecond<'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
My question is why can't f# infer what type 'a' is when ISecond is derived, since the information is embedded in 'First'?
For example in the following code the compiler could infer that 'a' is a string.
type First () =
interface IFirst<string> with
member x.Data = ""
type Second () =
interface ISecond<First> with
member x.SomeData = ""
member x.First = First()
Is there any way around this or does ISecond have to take two type parameters?
EDIT: I Am aware that ISecond can take two type parameters (note the last line of my initial question). To make it clearer what I mean consider the following code
type IFirst<'a> = interface end
type ISecond<'First, 'a when 'First :> IFirst<'a>> = interface end
type First () =
interface IFirst<string>
type Second () =
interface ISecond<First, int>
It gives the error "This expression was expected to have type string but here has type int", meaning the compiler knows that 'a' is a string, yet I still have to declare it as such. I wish to know why this is the case and whether there is a workaround without specifying the second type parameter.
Your definition of ISecond is incorrect: you're mentioning some type 'a, but not defining it. In other words, ISecond actually has two generic parameters - 'First and 'a, but you've only defined one of them.
This would work:
type ISecond<'a, 'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
But then, of course, you'll need to amend your definition of Second as well:
type Second () =
interface ISecond<string, First> with
member x.SomeData = ""
member x.First = First()
I think you have conflated two different questions. One is:
Do a type definition's generic parameters all need to be explicit?
The answer is yes. This has nothing to do with type inference, it's just how type definitions work in F# - the type parameter 'a will only be in scope to be used as an argument to IFirst<_> if it's also a parameter of ISecond.
The other question is:
Can the compiler infer a super-type's type parameters when defining a subtype?
Here, the answer is slightly more subtle. When defining a class type, the answer is that it is a syntactic requirement that you specify all of the type parameters. If you try something like:
type Second() = interface ISecond<First,_> with ...
you'll get the error message
error FS0715: Anonymous type variables are not permitted in this declaration
However, there are other contexts where the parameters can be inferred without issue:
let second() = { new ISecond<_,_> with
member x.SomeData = ""
member x.First = First() }
Here you can see that when using an object expression both of the parameters can be inferred.
I am trying to check if a variable is of a certain type like so:
let s = "abc"
let isString = s :? string
but in F# interactive, I get the following error:
error FS0016: The type 'string' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.
Why is this happening? I expect isString to be a bool.
Because you are trying with a sealed type.
Try instead this:
let s = box "abc"
let isString = s :? string
There is no point in doing this coercion tests with sealed types since they can't have any subtype and that's what the error message is telling you.
The box keyword will always return an object, whether the source is reference type (as in this case) or a value type, in which case it will "box" it.
The following yields
This construct causes code to be less generic than indicated by the type annotations. The type variable 'P has been constrained to be type 'bool'.
for the right side of the let myValue = expression, and
This code is less generic than required by its annotations because the explicit type variable 'P' could not be generalized. It was constrained to be 'bool'.
for the generic <'P> in the Value method:
type MyTypeA<'T> (myObject : 'T) as this =
let myValue = this.Value<bool> "SomeBooleanProperty"
member this.Value<'P> name = typeof<'T>.GetProperty(name, typeof<'P>).GetValue(myObject, null) :?> 'P`
However, this compiles just fine and yields no warnings or errors:
type MyTypeB<'T> (myObject : 'T) as this =
member this.Value<'P> name = typeof<'T>.GetProperty(name, typeof<'P>).GetValue(myObject, null) :?> 'P
member this.Method<'P> name = this.Value<'P> name
What's going on, here? Why, in the first example, is the method recognized in the assignment of the private value, but not as a legitimately generic method?
The warning (FS0064) is raised by a call to CheckWarnIfRigid function inside SolveTyparEqualsTyp fun from ConstraintSolver.fs.
After the warning is raised, SolveTyparEqualsTyp will continue (since there is no error so far) to solve type constraints.
The comment of SolveTyparEqualsTyp is :
/// Add the constraint "ty1 = ty" to the constraint problem, where ty1 is a type variable.
/// Propagate all effects of adding this constraint, e.g. to solve other variables
This leads to error FS0663 for member Value definition in OP's example. Followed by error FS0660.
For some reason I ignore, some propagation occurs.
Maybe type inference is too aggressively performed.
#jpe and other comments below OP's question contain more interesting clues.
I have a bunch of modules that export an IModule interface. So in the main program I have no problems
...
let mutable modules = Seq.empty
[<ImportMany>]
member x.Modules
with get():IEnumerable<Lazy<IModule, IModuleData>> = modules
and set(a) = modules <- a
...
But now I need to expose an interface back to those modules. So each module will import a single interface
...
let mutable parent:IParent = ?
[<Import>]
member x.Parent
with get():IParent = parent
and set(a) = parent <- a
...
So my problem is how do I go about creating my mutable "parent" when I have no initial value for it? Also, is this the appropriate way to expose an API back to component parts?
Using Unchecked.defaultof<_> should do the trick, but it means that you're circumventing the F# type system, which may be a dangerous thing to do - the system tries to prevent you from accidentally dereferencing null values (and getting NullReferenceException).
Types that are declared in F# don't have null as a proper value, which is an attempt to eliminate the usual errors caused by null. The clean F# approach is to use option types to represent the fact that a value is missing:
let mutable parent:option<IParent> = None
[<Import>]
member x.Parent
with get():IParent =
match parent with
| Some p -> p
| None -> failwith "TODO: Throw some reasonable exception here!"
and set(a) = parent <- Some(a)
If you just want to say that IParent can have a null value (perhaps because you need to use it in some C# code that will ignore the F# restriction anyway), then you can mark the type definition using a special attribute that allows using null with the type.
[<AllowNullLiteral>]
type IParent =
abstract DoStuff : unit -> unit
Then you can write let mutable parent:IParent = null. The benefit of this approach is that you can also easily check whether a value is null (using just if parent <> null then ...) which is not that obvious when you use Unchecked.defaultof<_>.
let mutable parent = Unchecked.defaultof<IParent>
should do the trick.
Following up on what Tomas explained, you should probably put your imports directly into your constructor. That will allow your code to be a bit more idiomatic.
I've been struggling to get this to compile for about an hour. It must be something stupid. Can you spot it?
in my lib project:
namespace TravelerStuff
open System
type Traveler =
abstract GetData : unit -> unit
type public DeltaTraveler() =
interface Traveler with
member v.GetData () =
printf "hello"
and in my console test app:
[<EntryPoint>] let main _ =
let traveler = new TravelerStuff.DeltaTraveler()
traveler.GetData // this line won't compile: (The field, constructor or member 'GetData' is not defined)
As gradbot says, F# doesn't currently implicitly convert values to interfaces when searching for members. Also, F# only uses explicit interface implementation (as known from C#) and not implicit implementation where members are not only compiled as implementation of an interface, but also as ordinary (directly visible) members of the type.
Aside from casting, you can duplicate the member in the type definition:
type DeltaTraveler() =
member v.GetData () = printf "hello"
interface Traveler with
member v.GetData () = v.GetData()
Also, if you just want to implement an interface, but don't need to add any members, you can use F# object expressions (which are more lightweight):
let deltaTraveler() =
{ new Traveler with
member v.GetData () = printf "hello" }
// The function directly returns value of type 'Traveler'
let t = deltaTraveler()
t.GetData()
You need to upcast. F# currently won't do it for you in this situation.
(traveler :> TravelerStuff.Traveler).GetData()
// open the namespace to reduce typing.
open TravelerStuff
(traveler :> Traveler).GetData()
Snip from F# docs.
In many object-oriented languages,
upcasting is implicit; in F#, the
rules are slightly different.
Upcasting is applied automatically
when you pass arguments to methods on
an object type. However, for let-bound
functions in a module, upcasting is
not automatic, unless the parameter
type is declared as a flexible type.
For more information, see Flexible Types (F#).