7

So i have this:

open System
open System.Linq
open Microsoft.FSharp.Collections
type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (First : List<'a>) (Second : List<'a>) =
        First.Concat(Second)

let a = [1; 2; 3; 4; 54; 9]
let b = [3; 5; 6; 4; 54]


for x in List.(+) a b do
    Console.WriteLine(x)

and I want to convert the last line into

for x in a + b do
    Console.WriteLine(x)

but doing so gives me a

The type 'int list' does not support any operands named '+'

The documentation and examples on the web are flakey, and despite my google-fu i have been unable to get it to work. Basically, coming from a python background, I want to get my list manipulation syntax as terse as I am used to: it should not need more than 1 character in infix notation.

Li Haoyi
  • 15,330
  • 17
  • 80
  • 137

5 Answers5

9

Note that @ is already a 1-char infix operator to concat lists.

Brian
  • 117,631
  • 17
  • 236
  • 300
8

First, overriding operators should be declared in the tuple form, not in the carried form. In your case:

type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (first: List<'a>, second: List<'a>) =
        first.Concat(second)

Second, after you fix that, the compiler raises the "Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead." warning. There are some workarounds which have been discussed thoroughly in Overload operator in F#: (/).

Community
  • 1
  • 1
pad
  • 41,040
  • 7
  • 92
  • 166
8

Actually there is a way to 're-wire' existing operators, using static constraints and overloads.

type ListExtension = ListExtension with
    static member        (?<-) (ListExtension, a , b) = a @ b
    static member inline (?<-) (ListExtension, a , b) = a + b

let inline (+) a b = (?<-) ListExtension a b

// test

let lst = [1;2] + [3;4]
// val lst : int list = [1; 2; 3; 4]

let sum = 1 + 2 + 3 + 4
// val sum : int = 10

By using the ternary operator the static constraints will be automatically inferred, another option would be to create a method and write the constraints by hand. The first overload cover the case you want to add (lists), the second covers the existing definition.

So now in your code you can do:

for x in (+) a b do
    Console.WriteLine(x)

And that will not break existing (+) for numeric types.

Gus
  • 25,839
  • 2
  • 51
  • 76
3

As pointed out by the other answers, you cannot add implementation of + to an existing type, because extension members are ignored and standalone let binding hides the default (overloaded) implementation.

If you wanted to use + (which is not really needed because F# library contains operator @), you would have to write wrapper for F# list that supports the operator directly:

open System.Collections
open System.Collections.Generic

/// Wrapper for F# list that exposes '+' operator and 
/// implements 'IEnumerable<_>' in order to work with 'for'
type PlusList<'T>(list : list<'T>) =
  member x.List = list
  static member (+) (first : PlusList<'a>, second : PlusList<'a>) =
    first.List @ second.List
  interface IEnumerable with
    member x.GetEnumerator() = (list :> IEnumerable).GetEnumerator()
  interface IEnumerable<'T> with
    member x.GetEnumerator() = (list :> IEnumerable<_>).GetEnumerator()

// Simple function to wrap list
let pl l = PlusList<_>(l)

let a = pl [1; 2; 3; 4; 54; 9]
let b = pl [3; 5; 6; 4; 54]

for x in a + b do
  System.Console.WriteLine(x)
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
0

I think operator overloading using extension method doesn't work. What you can do is define a global operator overload for list (+) using:

let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s) 
Ankur
  • 33,367
  • 2
  • 46
  • 72
  • 1
    It works, but it overrides all (+) operators of any type. So you cannot call 2 + 3 for integer anymore. It's against the idea of operator overloading. – pad Oct 08 '11 at 08:37
  • Ops.. you are right ... its weird that F# compiler doesn't use the type of values used to determine the correct operator to use. – Ankur Oct 08 '11 at 08:47
  • In that vein, does that mean F# doesn't let you overload a method with a multiple signatures and then pick the correct one at each point of use (a.l.a. Java)? – Li Haoyi Oct 08 '11 at 15:26
  • This limitation only applies to let-bound functions. You can still overload member functions in the usual way. – Frank Oct 08 '11 at 22:29