7

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.)

TeaDrivenDev
  • 6,591
  • 33
  • 50
  • I can't seem to reproduce your error. Maybe you could provide a repo demonstrating the error. – Ringil Jul 27 '16 at 23:02
  • 1
    Isn't the existing `Seq.cast` already a `Enumerable.OfType` in disguise ? – Sehnsucht Jul 28 '16 at 05:54
  • 1
    @Sehnsucht `OfType` is the same as `.Where(x => x is typeof(T))`, e.g. filter by type. Follow my first link from the answer and you will see both `OfType` and `Cast` implementations on the same screen. – V.B. Jul 28 '16 at 08:03
  • @V.B. yeah I mixed the two in my mind ^^ – Sehnsucht Jul 28 '16 at 08:16

1 Answers1

7

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>'
Community
  • 1
  • 1
V.B.
  • 6,236
  • 1
  • 33
  • 56