Enforce explicit type arguments - f#

I have a generic function where it's easy to get the compiler to infer the wrong type parameters. The type parameters only control the return type, and if I accidentally miss off a type annotation, the compiler infers obj.
How can I make it a compile-time error to call my function without giving it explicit type parameters? The Unchecked.defaultof function works the way I'd like:
> Unchecked.defaultof;;
Unchecked.defaultof;;
^^^^^^^^^^^^^^^^^^^
stdin(1,1): error FS0685: The generic function 'defaultof' must be given explicit type argument(s)

The defaultof function uses a special attribute. The F# source code is, once again, useful. The implementation of the function is in prim-types.fs, but the attribute is added in the interface file prim-types.fsi. A combined declaration would be:
[<RequiresExplicitTypeArguments>]
let inline unsafeDefault<'T> : 'T = (# "ilzero !0" type ('T) : 'T #)
The inline IL (# ... #) is limited to F# core, but the declaration is something anybody can use.
You can find the attribute in section 16 (page 217) of the F# specification:
When applied to an F# function or method, indicates that the function or method must be given explicit type arguments when used. For example, typeof<int>.
This attribute should be used only in F# assemblies.

RequiresExplicitTypeArgumentsAttribute should help

Related

Passing "unit" as type parameter to generic class in F#

I have an interface which takes a generic parameter and has an abstract method
type MyInterface<'a> =
abstract member abstractMethod: 'a -> 'a
...and I have a derived class which inherits from base class using unit for the type parameter
type Derived() =
interface MyInterface<unit> with
override this.abstractMethod (_) = ()
but the compiler complains that
Error The member 'abstractMethod : unit -> unit' does not have the
correct type to override the corresponding abstract method.
If I use another type instead of unit, int for example, the code compiles.
Is this a bug in the compiler? Is there a workaround for it?
Thanks!
unit is "special" in terms of interop: when a function takes a unit as parameter, it is compiled to IL as a parameterless function, and when a function returns a unit as result, it is compiled as a void function. As a result of this trickery, you can't really use unit as a "general-purpose" type in interop settings (such as classes and interfaces).
In fact, when I try to compile your code on my machine, I get a different error:
The member 'abstractMethod : unit -> unit' is specialized with 'unit'
but 'unit' can't be used as return type of an abstract method
parameterized on return type.
Which is trying to say roughly what I described above.
(I'm not sure why you're getting what you're getting; perhaps you're using an older version of F#)

What does it mean to upcast a value to the _ wildcard?

What does it mean to implement a wildcard at the end of a case statement?
Take the following syntax:
match imp req with
| Success () -> this.Ok () :> _
Is this the same as:
| Success () -> this.Ok () :> IHttpActionResult
What is the advantage of writing that type of syntax?
Here's the context of my question:
type PushController (imp) =
    inherit ApiController ()
    member this.Post (portalId : string, req : PushRequestDtr) : IHttpActionResult =
        match imp req with
        | Success () -> this.Ok () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
The operator :> performs an static upcast to the type specified by the expression to its right side. The syntax for this operator is:
:> expression
That would be, for your example:
some_value :> IHttpActionResult
this tells the compiler that some_value is in fact an object that implements IHttpActionResult.
But according to the F# documentation:
When you use the upcast operator, the compiler attempts to infer the
type you are converting to from the context. If the compiler is unable
to determine the target type, the compiler reports an error.
https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/casting-and-conversions-%5Bfsharp%5D
Because the only type that can be returned by the Post method is IHttpActionResult, you can let the compiler infer it.
So, in this context this:
:> _
Is equivalent to:
:> IHttpActionResult

What does a colon after a tuple but before another type mean within a method signature?

What does a colon that's positioned after a tuple but before another type mean within a method signature?
Here's the syntax:
member this.Post (portalId : string, req : PushRequestDtr) : IHttpActionResult =
Here's the context:
type PushController (imp) =
    inherit ApiController ()
    member this.Post (portalId : string, req : PushRequestDtr) : IHttpActionResult =
        match imp req with
        | Success () -> this.Ok () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
Specifically, what does this method signature mean?
Does this method take two parameters or one parameter?
I understand this:
(portalId : string, req : PushRequestDtr)
But I'm confused about this syntax that's appended to the end of it:
: IHttpActionResult
That would be the return type, i.e. the type of the value returned by the method.
From the F# online docummentation:
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/members/methods
// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-nameparameter-list [ : return-type ]=
method-body
In this case return_type is IHttpActionResult, which means this method will return an object that implements the IHttpActionResult interface.
Also, although (portalId : string, req : PushRequestDtr) looks like a tuple (and in a certain way it is syntax-wise) it is not, in fact, treated as a tuple. In this case, this is a specific F# syntax for declaring method arguments while defining a method of a F# object. This is the part represented by method-nameparameter-list in the F# method template declaration. This means that the Post method receives two arguments: portalId and req, not a single argument as a tuple.
Specifically, this syntax of a list of arguments that looks like a tuple but that they are not a tuple has to be used when declaring method arguments instead of function arguments. The member keyword is the one that makes this line a method declaration instead of a function declaration.
--
Regarding the :> operator: This is a cast operator. More specifically a upcasting operator (which changes the type of a more derived type to the type of some higher type in the type hierarchy).
In this case, it is being used to explicitly tell the compiler that each branch in the match expression will return some type that is derived (or that implements) IHttpActionResult. I am not quite sure why this cast is needed (something to do with F# not being able to infer the correct type in this context, see this other question: Type mismatch error. F# type inference fail?) but in fact, it is casting every possible return value to IHttpActionResult which is the method's return type.
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/casting-and-conversions

Where is the definition of "TypeIdentifier"

I would need to write a function that performs operations in RTTI mode on data, of which I do not know a priori the type.
I tried to write a function like this:
function doSomething (T: TypeIdentifier): Boolean;
as when using the expression TypeInfo(T) he expects as a type parameter TypeIdentifier
But when I try to compile I get an error because the type TypeIdentifier not recognized.
Someone could explain me how I can do to send a data type of which I know the type only at runtime?
Thanks to the availability.
Enzo
TypeInfo is an intrinsic function that uses compiler magic. That is the compiler generates the code that implements the function rather than it being part of the runtime library.
You don't have access to such magic. You cannot create a function that accepts a type identifier in the manner of TypeInfo. So you need to make your function accept what TypeInfo returns, a pointer to the type info, PTypeInfo.
You'd call your function like this:
DoSomething(TypeInfo(SomeTypeIdentifier));
Now technically, you may notice that TypeInfo returns a value of type Pointer. That's because PTypeInfo is defined in another unit, TypInfo and the System unit where all intrinsics are defined is not allowed to use TypInfo. But as stated in the documentation linked above, TypeInfo returns a pointer to TTypeInfo.

Getting FS0035 => Construct is deprecated

In a fsyacc based project, I have this line:
type 'a cucomment = string
This is the full error description I'm getting:
CALast.fs(117,9): error FS0035: This construct is deprecated: This
type abbreviation has one or more declared type parameters that do not
appear in the type being abbreviated. Type abbreviations must use all
declared type parameters in the type being abbreviated. Consider
removing one or more type parameters, or use a concrete type
definition that wraps an underlying type, such as 'type C<'a> = C of
...'.
Any idea how to solve this?
F# no longer allows type aliases that add generic type parameters to a type without declaring a new type. If you want to define a generic type that wraps some other type, you have to use some constructor. For example, you can use single-case discriminated union:
type 'a Cucomment = CC of string
Unfortunately, this means that you'd have to change all code that uses the type to unwrap the value using pattern matching or by adding Value member to the type.
The only case where generic type aliases are allowed is when you declare a version of type with units of measure, which requires a special attribute. However, this is probably not going to work for you (because units behave quite differently):
[<MeasureAnnotatedAbbreviation>]
type 'a Cucomment = string
If this is in some code generated by fsyacc, then that's a bug in fsyacc that should be fixed (I think this was quite recent change). In that case, report it to fsbugs at microsoft dot com.

Resources