Оператор перегрузки + в F #

Итак, у меня есть это:

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)

и я хочу преобразовать последнюю строку в

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

но это дает мне

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

Документация и примеры в сети отрывочны, и, несмотря на мой гугл-фу, я не смог заставить его работать. По сути, исходя из фона Python, я хочу, чтобы мой синтаксис управления списком был столь же кратким, как я привык: для него не должно быть более 1 символа в инфиксной нотации.


person Li Haoyi    schedule 08.10.2011    source источник


Ответы (5)


arrow_upward
7
arrow_downward

Во-первых, замещающие операторы следует объявлять в форме кортежа, а не в переносимой форме. В твоем случае:

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

Во-вторых, после того, как вы это исправите, компилятор выдает предупреждение "Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.". Есть некоторые обходные пути, которые подробно обсуждались в Оператор перегрузки в F #: (/).

person pad    schedule 08.10.2011
comment
@LiHaoyi, но есть способ обойти это ограничение, см. Мой ответ. - person Gus; 11.09.2014

arrow_upward
9
arrow_downward

Обратите внимание, что @ уже является инфиксным оператором с 1 символом для объединения списков.

person Brian    schedule 08.10.2011
comment
О, я этого не знал. Спасибо! - person Li Haoyi; 08.10.2011

arrow_upward
8
arrow_downward

На самом деле есть способ «переназначить» существующие операторы, используя статические ограничения и перегрузки.

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

При использовании тернарного оператора статические ограничения будут автоматически выведены, другой вариант - создать метод и написать ограничения вручную. Первая перегрузка охватывает случай, который вы хотите добавить (списки), вторая - существующее определение.

Итак, теперь в вашем коде вы можете:

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

И это не нарушит существующие (+) для числовых типов.

person Gus    schedule 04.12.2011

arrow_upward
3
arrow_downward

Как указано в других ответах, вы не можете добавить реализацию + к существующему типу, потому что члены расширения игнорируются, а автономная привязка let скрывает реализацию по умолчанию (перегруженную).

Если вы хотите использовать + (что на самом деле не нужно, потому что библиотека F # содержит оператор @), вам придется написать оболочку для списка F #, которая поддерживает оператор напрямую:

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)
person Tomas Petricek    schedule 08.10.2011

arrow_upward
0
arrow_downward

Я думаю, что перегрузка оператора с использованием метода расширения не работает. Что вы можете сделать, так это определить перегрузку глобального оператора для списка (+), используя:

let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s)
person Ankur    schedule 08.10.2011
comment
Он работает, но отменяет все (+) операторы любого типа. Таким образом, вы больше не можете вызывать 2 + 3 для целого числа. Это против идеи перегрузки оператора. - person pad; 08.10.2011
comment
Упс ... вы правы ... странно, что компилятор F # не использует тип значений, используемых для определения правильного оператора. - person Ankur; 08.10.2011
comment
В этом смысле означает ли это, что F # не позволяет вам перегружать метод несколькими сигнатурами, а затем выбирать правильную в каждой точке использования (например, Java)? - person Li Haoyi; 08.10.2011
comment
Это ограничение применяется только к функциям с привязкой к let. Вы по-прежнему можете перегружать функции-члены обычным способом. - person Frank; 09.10.2011