Скажем, у меня есть список и набор, и я хочу сделать с ними кое-что:
let outA = inA |> List.map(fun x -> x + 1) |> List.filter(fun x -> x > 10)
let outB = inB |> Set.map(fun x -> x + 1) |> Set.filter(fun x -> x > 10)
Теперь, очевидно, A обрабатывает списки и B обрабатывает множества. Тем не менее, мне очень неприятно писать List.
List.
снова и снова: это не только подробный, повторяющийся шаблон, который не передает никакой информации и мешает читать функциональность кода, это также аннотацию типа де-факто, которую я должен отслеживать и сохранять в синхронизации с остальной частью моего кода.
То, что я хочу сделать, это что-то вроде следующего:
let outA = inA |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
let outB = inB |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
С компилятором, зная, что inA - это список, а inB - это набор, и, следовательно, все операции относятся к правильному классу, и поэтому outA является списком, а outB является множеством. Я частично могу добиться этого с помощью Seq:
let map(x) =
Seq.map(x)
let filter(x) =
Seq.filter(x)
И я могу это точно написать. Проблема заключается в том, что он вставляет все в последовательности, и я больше не могу выполнять операции списка/набора. Аналогично,
let outA = inA.Select(fun x -> x + 1).Where(fun x -> x > 10)
let outB = inB.Select(fun x -> x + 1).Where(fun x -> x > 10)
Также удаляет все это, но затем бросает все на IEnumerable. Мне удалось получить это довольно хорошо, преобразовывая все статические методы в методы экземпляра с помощью расширений:
type Microsoft.FSharp.Collections.List<'a> with
member this.map(f) = this |> List.map(f)
member this.filter(f) = this |> List.filter(f)
let b = a.map(fun x -> x + 1).filter(fun x -> x > 10)
но я подозреваю, что столкнутся с описанными здесь проблемами типа-вывода: Цепочка методов vs | > Оператор труб? Я действительно не знаю; Я не знаком с тем, как работает алгоритм вывода типа.
Суть в том, что я собираюсь сделать все эти операции списка/набора/массива/сокращения/фильтрации, и я хочу, чтобы они выглядели как можно более красивыми и чистыми. Прямо сейчас, кроме отвлечения меня от важных бит в выражении (т.е. "Карта" и лямбда), они также предоставляют аннотации типа де-факто в месте, где компилятор должен радостно-хорошо знать, какая коллекция я прошел в есть.
Кроме того, это именно то, что предназначено для наследования и полиморфизма ОО, поэтому мне было интересно, есть ли какой-то эквивалентный функциональный шаблон, который я не знаю, что бы решить его так же изящно. Возможно, я мог бы выполнить проверку типа в моем собственном статическом map
и выполнить соответствующую функцию типа map
на входе?
Есть ли у кого-нибудь лучшее решение, чем те, которые я уже пробовал, или я натыкаюсь на фундаментальное ограничение механизма вывода типа F #, которое я должен просто принять и продолжить?