Reputation: 119
I have a Set<String*String>
, and I'm trying to write a function that takes all the elements of that Set and return a Set<String>
. My idea is to use Set.fold, and have an empty set accumulator and take the union of the two sets, but I'm running into problems. Here is the code:
type Chart = Set<Country*Country>;;
let countriesInChart (chart : Chart) =
Set.fold(fun (x,y) set -> Set.union [x;y] set ) chart []
But I get this error
Set.fold(fun (x,y) set -> Set.union [x;y] set ) chart [];;
--------------------------------^^^^^^^^^^^^^^^^^^^
error FS0001: This expression was expected to have type
'a * 'b
but here has type
Set<'c>
Upvotes: 0
Views: 444
Reputation: 6629
Look at your types and function signatures.
Set.fold
takes a 'State -> 'T -> 'State
as the folder
function. 'State
is the type that you're folding into and that will be the eventual return value, so in this case, you want it to be of type Set<Country>
.
That means your lambda can't be right, because the first argument is a tuple. So we should probably switch the arguments of that lambda:
let countriesInChart (chart : Chart) =
Set.fold(fun set (x,y) -> Set.union [x;y] set ) chart []
Compiling that gives us
(96,39): error FS0001: This expression was expected to have type
Set<'a>
but here has type
'b list
(96,39) in this case is the Set.union
function, and of course that is not used correctly, because it requires two sets, but we're passing it one set and a list. We can create a set from the list using Set.ofList
:
let countriesInChart (chart : Chart) =
Set.fold(fun set (x,y) -> [x; y] |> Set.ofList |> Set.union set) chart []
Again, we're getting a different error, so we're probably making progress:
(96,80): error FS0001: This expression was expected to have type
Set<(Country * Country) * (Country * Country)>
but here has type
'a list
(96,80) is the empty list at the end of the line - and of course, that's wrong, because the third argument to Set.fold
needs to be Set<'T>
. The set replacement for an empty list would be Set.empty
, so let's go with that:
let countriesInChart (chart : Chart) =
Set.fold(fun set (x,y) -> [x; y] |> Set.ofList |> Set.union set) chart Set.empty
It compiles! But as you found, it returns Set<Country * Country>
instead of just Set<Country>
.
Cases like this are when type inference makes it a little harder to see what's going on, so we should go ahead and add type annotations where we know exactly what the types need to be. The most obvious place is the return type of the function:
let countriesInChart (chart : Chart) : Set<Country> =
Set.fold(fun set (x,y) -> [x; y] |> Set.ofList |> Set.union set) chart Set.empty
Now the error is:
(96,74): error FS0001: Type mismatch. Expecting a
Set<Country>
but given a
Chart
The type 'Country' does not match the type 'Country * Country'
That error is for the second argument of Set.fold
, and the reason is that once again, the arguments are in the wrong order. The signature of Set.fold
is ('State -> 'T -> 'State) -> 'State -> Set<'T> -> 'State
. If we look at what we already have, 'State
in this case is Set<Country>
, and 'T
is Country * Country
. That means Set.empty
needs to be the second and chart
the last argument, and so we arrive at
let countriesInChart (chart : Chart) =
Set.fold(fun set (x,y) -> [x; y] |> Set.ofList |> Set.union set) Set.empty chart
The most important rule of functional programming is this: Let the types guide you! ;-)
Upvotes: 4
Reputation: 1109
Try this one:
let f (chart: Chart) =
Set.fold (fun (x:Set<string>) (a,b) -> x |> Set.add a |> Set.add b) Set.empty chart
I'm not sure if the type annotation is necessary but it does force the output to be a Set<string>
.
Upvotes: 0