Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# '+' operator overload and List.fold

I'm trying to use List.fold on a record type that defines an operator overload for +, but I'm getting a type mismatch error when trying to use the (+) operator as the lambda passed to fold. Here's a simplified snippet that exemplifies my problem:

// a record type that also includes an overload for '+'
type Person = 
    { Name : string; Age: int }
    static member ( + ) (x: Person, y: Person) = x.Age + y.Age

the + overload works just fine

> jen + kevin;;
val it : int = 87

but say I have a list of person:

> let people = [kevin;jen];;

I can't use List.fold to sum all the ages:

> List.fold (+) 0 people;;

List.fold (+) 0 people;;
----------------^^^^^^

error FS0001: Type constraint mismatch. The type 
    int    
is not compatible with type
    Person    
The type 'int' is not compatible with the type 'Person'

I'm guessing the problem is that F# isn't able to discern the overload of + when passed in this fashion, since fold is implicitly typing the list to int because I used '0' as the accumulator. I'm not sure if it's possible to get my custom operator overload to work correctly, and if it is possible, what I'm missing to make it happen. (I'm assuming it is possible to make this work because you can use + on floats).

edit

I understand that the problem is the the type mismatch. As JaredPar writes, I get that I could write a lambda to take two person records and add the ages. That's not my point. The issue is that it seems to me there should be a way to get the + operator overload I already wrote to be acknowledged by fold as a valid overload.

another edit

Thanks all for your input. One thing that's becoming clear is that it isn't possible to do what I want, but that's fine. I learned something! What I'm seeing is that the resolution of operator overloads is such that they don't work in every context--so with fold there is no seamless way to make + passed as a lambda work just like it would when used as an infix ala jen + kevin. It makes total sense why this doesn't work right. The resolutions people have suggested to resolve this problem basically are one-off's to handle the particular issue of fold--what I'm really after is how to get the correct operator overload to get picked for every situation (i.e. foldback, etc)--I didn't want to have to write a bunch of special case code for working over lists. It's pretty clear not what F#'s operator overload resolution has some limitations that make it work to a skin-deep level, which is fine.

like image 383
Kevin Won Avatar asked Nov 23 '25 19:11

Kevin Won


1 Answers

The List.fold function takes a lambda / function of type State -> T -> State. The + operator in this case has type Person -> Person -> int which is incompatible with the signature. This is why you're getting the error.

To fold the ages try the following

people |> List.fold (fun sum p -> sum + p.Age) 0

One way to use the + operator here as part of the fold is to map the Person into the Age property and then use fold against the int + operator.

people
|> Seq.ofList
|> Seq.map (fun p -> p.Age)
|> Seq.fold (+) 0
like image 197
JaredPar Avatar answered Nov 26 '25 12:11

JaredPar