Перегрузка + оператор в 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 символа в инфиксной нотации.
5 ответов
Во-первых, переопределяющие операторы должны быть объявлены в форме кортежа, а не в переносимой форме. В твоем случае:
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#: (/).
Обратите внимание, что @
уже является 1-символьным инфиксным оператором для объединения списков.
На самом деле есть способ "пересоединить" существующих операторов, используя статические ограничения и перегрузки.
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)
И это не сломает существующее (+)
для числовых типов.
Как указано в других ответах, вы не можете добавить реализацию +
к существующему типу, потому что члены расширения игнорируются и автономны 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)
Я думаю, что перегрузка операторов с использованием метода расширения не работает. Что вы можете сделать, это определить глобальную перегрузку оператора для списка (+), используя:
let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s)