F# Why can't I use the :? operator in F# interactive? - f#

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.

Related

ContainsKey returns unit and not bool?

It's odd to me that the following code throws a compile time error. I'm not sure why ContainsKey is returning unit. The documentation says it returns bool.
open System.Collections.Generic
let mydict = new Dictionary<string,'a>()
if(mydict.ContainsKey "mykey") then
mydict.["mykey"] = newkey
error FS0001: This expression was expected to have type
'bool' but here has type
'unit'
Am I missing something here?
if is an expression so both branches must have the same type. If the else branch is not specified an empty one of type unit is inserted. This means your then branch must also have type unit. However mydict.["mykey"] = newkey has type bool. If you want to insert a new value for mykey you should use <- instead of =:
if(mydict.ContainsKey "mykey") then
mydict.["mykey"] <- newkey

Nested Generic Type Inference in f#

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.

Trying to understand inferred type constraints

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.

Why do I get an error that sprintf is incompatible with type string? [duplicate]

I feel like a total noob for having to ask this but it's got me stumped.
I set a format string like this:
let fs = "This is my format test %s"
Then I attempt to use it like so:
let s = sprintf fs "testing"
When I do so I get this error:
//stdin(26,17): error FS0001: The type 'string' is not compatible with the type 'Printf.StringFormat<('a -> 'b)>'
So I then tried this:
let s = sprintf (Printf.StringFormat fs) "test"
to which the REPL responded:
//stdin(28,18): error FS1124: Multiple types exist called 'StringFormat', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. 'StringFormat<_>'.
So I then tried this:
let s = sprintf (Printf.StringFormat<string> fs) "test"
And I get this:
//stdin(29,18): error FS0001: The type ''a -> 'b' does not match the type 'string'
Am I missing something painfully obvious? This is using F# 3.0 on the Mac from the Xamarin Studio F# Interactive Window.
So you actually need to create a StringFormat which has a function type as follows
> sprintf (Printf.StringFormat<string->string>("Hello %s")) "World";;
val it : string = "Hello World"
In Section 6.3.16 of the spec, an example of this is shown.

How Do I Use A Variable As The Formatting String With Sprintf?

I feel like a total noob for having to ask this but it's got me stumped.
I set a format string like this:
let fs = "This is my format test %s"
Then I attempt to use it like so:
let s = sprintf fs "testing"
When I do so I get this error:
//stdin(26,17): error FS0001: The type 'string' is not compatible with the type 'Printf.StringFormat<('a -> 'b)>'
So I then tried this:
let s = sprintf (Printf.StringFormat fs) "test"
to which the REPL responded:
//stdin(28,18): error FS1124: Multiple types exist called 'StringFormat', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. 'StringFormat<_>'.
So I then tried this:
let s = sprintf (Printf.StringFormat<string> fs) "test"
And I get this:
//stdin(29,18): error FS0001: The type ''a -> 'b' does not match the type 'string'
Am I missing something painfully obvious? This is using F# 3.0 on the Mac from the Xamarin Studio F# Interactive Window.
So you actually need to create a StringFormat which has a function type as follows
> sprintf (Printf.StringFormat<string->string>("Hello %s")) "World";;
val it : string = "Hello World"
In Section 6.3.16 of the spec, an example of this is shown.

Resources