Вопрос

I'm new to F# and today I saw the member constraint feature of F#.

I was thinking of if one could (ab)use it in the following way...

Say I want to iterate get the texts of the items in a ListView. I could do it like this:

let listView = new System.Windows.Forms.ListView()
let itemTexts = 
    listView.Items
    |> Seq.cast<System.Windows.Forms.ListViewItem>
    |> Seq.map (fun item -> item.Text)

The Seq.cast above is not type safe and kind of ugly.

What I was thinking was this:

> let item (i:int) = listView.Items.Item i;;

val item : int -> System.Windows.Forms.ListViewItem

So the type of the IEnumerable is in fact encoded into the class, just in a round-about way.

The example of member constraints I saw was the following:

type Cat() = 
    member this.Walk() = printfn "cat walk"
type Dog() = 
    member this.Walk() = printfn "dog walk"

let adapter() = 
    let cat = Cat()
    let dog = Dog()
    let inline walk (x : ^T) = (^T : (member Walk : unit->unit) (x))
    walk(cat)
    walk(dog)

I got the idea that it's a bit similar to what I'd want to do above.

Could this be used to create a function to be use like the following instead?

let itemTexts = 
    listView.Items
    |> Seq.asSeq
    |> Seq.map (fun item -> item.Text)

Edit: The idea was to implement a general Seq.asSeq extension method that'd take something that implements the non-generic IEnumerable and that has a strongly typed Item-member as above and then invoke Seq.cast with the corresponding type from the Item-member.

Here's my first fumbling attempts:

let listView = new System.Windows.Forms.ListView()
let items = 
    listView.Items

type cat() = 
    member this.Item (i:int) = "cat walk"
    interface System.Collections.IEnumerable with
       member this.GetEnumerator() = ((seq { yield (this.Item 0) }) :> System.Collections.IEnumerable).GetEnumerator()

type dog() = 
    member this.Item (i:int) = 6
    interface System.Collections.IEnumerable with
       member this.GetEnumerator() = ((seq { yield (this.Item 0) }) :> System.Collections.IEnumerable).GetEnumerator()

let cat = cat()
let dog = dog()

let inline asSeq (x : ^t) =
    let dummy _ = (^t : (member Item : int -> 'a) (x, 0))
    Seq.cast<'a> x

This almost works, but not for the real type!

> cat;;
val it : cat = seq ["cat walk"]
> asSeq cat;;
val it : seq<string> = seq ["cat walk"]
> asSeq dog;;
val it : seq<int> = seq [6]
> asSeq items;;

  asSeq items;;
  ------^^^^^

C:\...\stdin(5,7): error FS0001: The type 'System.Windows.Forms.ListView.ListViewItemCollection' does not support any operators named 'Item'
>

I guess it's my fake-types that do not properly reflect the .NET indexed properties...

Any help?

Edit 2: This actually works!

let inline asSeq (x : ^t) =
    let dummy _ = (^t : (member Item : int -> 'a with get) (x, 0))
    Seq.cast<'a> x
Это было полезно?

Решение

It sounds like you want something along these lines, in which case your code is nearly right.

Note that one issue is that you're attempting to use indexed properties interchangeably with methods, but you're not doing it quite right: for an indexed property named Item, the method's actual name will be get_Item (alternatively, you can use the syntax member Item : int -> 'a with get instead of using the get_Item method directly).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top