dict // turn it into keyvaluepair sequence |> Seq.map id |...">

Why does F # require type placeholders for ToDictionary?

the

[ 1,"test2" 3,"test" ] |> dict // turn it into keyvaluepair sequence |> Seq.map id |> fun x -> x.ToDictionary<_,_,_>((fun x -> x.Key), fun x -> x.Value) 

which fails to compile unless I explicitly use <_,_,_> after ToDictionary .
Intellisense works fine, but compilation fails: finding an object of an unspecified type based on information up to this point in the program So, it seems that Intellisense knows how to resolve the method call.

It looks like a hint

 |> fun x -> x.ToDictionary<_,_>((fun x -> x.Key), fun x -> x.Value) 

not working with

 Type constraint mismatch. The type 'b -> 'c is not compatible with type IEqualityComparer<'a> The type 'b -> 'c' is not compatible with the type 'IEqualityComparer<'a>' (using external F# compiler) 

x.ToDictionary((fun x -> x.Key), id)

works as expected

 let vMap (item:KeyValuePair<_,_>) = item.Value x.ToDictionary((fun x -> x.Key), vMap) 

I reproduced the behavior in FSI and LinqPad.

As a big fan and a greedy reader, Eric Lippert I really want to know what overload resolution (or, possibly, extension methods from different places) conflict here, what is the compiler confused?

+6
source share
2 answers

Although types are known in advance, the compiler gets confused between overloading, which takes an element selector and a comparator. The lambda is compiled in FSharpFunc instead of the standard delegate types in C #, for example, Action or Func , and the problems really move from one to the other. To make it work, you can:

Put type annotation for Func intruder

 fun x -> x.ToDictionary((fun pair -> pair.Key), (fun (pair : KeyValuePair<_, _>) -> pair.Value)) //compiles 

or specify the argument as a hint

 fun x -> x.ToDictionary((fun pair -> pair.Key), elementSelector = (fun (pair) -> pair.Value)) 

or make him choose a version with three arguments:

 x.ToLookup((fun pair -> pair.Key), (fun (pair) -> pair.Value), EqualityComparer.Default) 

Besides

In your example

 let vMap (item:KeyValuePair<_,_>) = item.Value x.ToDictionary((fun x -> x.Key), vMap) 

you obviously need to annotate vMap because the compiler cannot find out what type of property exists without another pass. For instance,

 List.map (fun x -> x.Length) ["one"; "two"] // this fails to compile 

This is one of the reasons why the pipe operator is so useful that it avoids type annotations:

 ["one"; "two"] |> List.map (fun x -> x.Length) // works List.map (fun (x:string) -> x.Length) ["one"; "two"] //also works 
+2
source

Short answer:

the ToDictionary extension method is defined as follows:

 static member ToDictionary<'TSource,_,_>(source,_,_) 

but called like this:

 source.ToDictionary<'TSource,_,_>(_,_) 

Long answer:

This is a signature of type F # of the function you are calling from msdn .

 static member ToDictionary<'TSource, 'TKey, 'TElement> : source:IEnumerable<'TSource> * keySelector:Func<'TSource, 'TKey> * elementSelector:Func<'TSource, 'TElement> -> Dictionary<'TKey, 'TElement> 

But I specified only two regular parameters: keySelector and elementSelector. Why does this have a source parameter ?!

The original parameter does not actually fit in the parenthesis, but is passed by saying x.ToDictionary, where x is the original parameter. This is actually an example of an extension . These methods are very natural in a functional programming language such as F #, but more unusual in an object-oriented language such as C #, so if you exit the C # world, it will be quite confusing. In any case, if we look at the C # header, itโ€™s a little easier to understand what is going on:

 public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector ) 

Thus, the method is determined by the "this" prefix on the first parameter, even if it is technically static. This basically allows you to add methods to already defined classes without recompiling or extending them. This is called prototyping. It's rare if you are a C # programmer, but languages โ€‹โ€‹like python and javascript make you aware of this. Take this example from https://docs.python.org/3/tutorial/classes.html :

 class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead'] 

The add_trick method is defined as self as the first parameter, but the function is called as d.add_trick ('roll over'). F # really does this naturally as well, but in a way that mimics the way a function is called. When you declare:

 member x.doSomething() = ... 

or

 member this.doSomething() = ... 

Here you add the doSomething function to the prototype (or class definition) of "x" / "this". So in your example, you actually have three type parameters and three regular parameters, but one of them is not used in the call. All you have left is to declare a key selection function and an element selector function that you did. That's why it looks weird.

+1
source

Source: https://habr.com/ru/post/1013826/


All Articles