Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type error related to generics depending on location of function

Tags:

generics

f#

I created a pipe-able ofType<'T> function for sequences based on Enumerable.OfType<'T>():

let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()

Using this within the same .fsx file works fine; it still does when I put it into a module:

module Seq =
    let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()

It stops working when I move it into another script file and (to be able to access it from elsewhere) wrap it in another top-level module:

module Prelude =
    open System.Linq

    module Seq =
        let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()

I reference this from my original script file, open the Prelude module and call the function like this:

let getXmlIncludes (xtype : Type) =
    xtype.GetCustomAttributes() |> Seq.ofType<XmlIncludeAttribute>

That causes Seq.ofType<XmlIncludeAttribute> to be marked as an error, with the message

error FS0001: Type mismatch. Expecting a
    Collections.Generic.IEnumerable<Attribute> -> 'a    
> but given a
    Collections.Generic.IEnumerable<Attribute> -> Collections.Generic.IEnumerable<XmlIncludeAttribute>    
The type 'obj' does not match the type 'Attribute'

The error remains the same when I move ofType<'T> directly into the Prelude module.

Why does this happen, and how can I make it not happen?

(I tried changing the _ seq type for the sequence parameter to 'TSeq seq, which results in the ever-popular

warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'TSeq has been constrained to be type 'obj'.

but doesn't change anything about the error.)

like image 400
TeaDrivenDev Avatar asked Jul 27 '16 21:07

TeaDrivenDev


People also ask

Which types can be used as arguments of a generic type?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).

How do I restrict a generic type in Java?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

What is generic type data?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


1 Answers

Enumerable.OfType<'T>() is not generic with regards to input parameter. After changing _ seq to non-generic IEnumerable the error disappears.

open System
open System.Collections
open System.Reflection
open System.Xml.Serialization

module Prelude =
    open System.Linq
    module Seq =
        let inline ofType<'T> (sequence : IEnumerable) = sequence.OfType<'T>()

open Prelude
let getXmlIncludes (xtype : Type) =
    xtype.GetCustomAttributes() |> Seq.ofType<XmlIncludeAttribute>

In your original code (sequence : _ seq) is constrained to seq<Attribute>, but F# does not support type covariance and cannot work with seq<XmlIncludeAttribute> as if it was seq<Attribute>, even though XmlIncludeAttribute inherits Attribute. But even if F# had supported covariance, your example would have worked only for this particular case plus only for types that inherit from Attribute.

You could clearly see the error if you try to use your Seq extension with a different type:

let getIntsAsUints (list : List<int>) =
    list |> Seq.ofType<uint32>

Script.fsx(21,13): error FS0001: The type 'List<int>' is not compatible with the type 'seq<Attribute>'
like image 57
V.B. Avatar answered Oct 06 '22 23:10

V.B.