Проблема со статически разрешаемыми параметрами типов заключается в том, что синтаксис громоздок.Вот пример:
let inline snippet< ^T when ^T : (member Id : string)
and ^T : (member Country : string)
and ^T : (member Tax : float) >
xs =
xs
|> Seq.filter (fun row -> ( ^T : (member Id : string) row) <> "---")
|> Seq.filter (fun row -> ( ^T : (member Country : string) row) <> "South America")
|> Seq.filter (fun row -> ( ^T : (member Tax : float) row) <> 0.0)
|> Seq.groupBy (fun row -> ( ^T : (member Country : string) row))
|> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> ( ^T : (member Tax : float) row))))
|> List.ofSeq
(я изменил вызов averageBy, поэтому он имеет больше смысла, чем вопрос в вопросе.)
Вы можете позволить компилятору выводить ограничения довольночем объявить их явно:
let inline snippet xs =
xs
|> Seq.filter (fun row -> ( ^T : (member Id : string) row) <> "---")
|> Seq.filter (fun row -> ( ^T : (member Country : string) row) <> "South America")
|> Seq.filter (fun row -> ( ^T : (member Tax : float) row) <> 0.0)
|> Seq.groupBy (fun row -> ( ^T : (member Country : string) row))
|> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> ( ^T : (member Tax : float) row))))
|> List.ofSeq
В качестве альтернативы вы можете потребовать, чтобы вызывающая сторона передавала функции-получатели значений:
let snippet2 getId getCountry getTax xs =
xs
|> Seq.filter (fun row -> getId row <> "---")
|> Seq.filter (fun row -> getCountry row <> "South America")
|> Seq.filter (fun row -> getTax row <> 0.0)
|> Seq.groupBy (fun row -> getCountry row)
|> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> getTax row))))
|> List.ofSeq
Это позволяет несколько упростить синтаксис:
let snippet2 getId getCountry getTax xs =
xs
|> Seq.filter (getId >> (<>) "---")
|> Seq.filter (getCountry >> (<>) "South America")
|> Seq.filter (getTax >> (<>) 0.0)
|> Seq.groupBy getCountry
|> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy getTax))
|> List.ofSeq
Вы можете объявить специфичные для типа экземпляры функции с частичным применением:
let snippetForCsvFile1 (xs : CsvFile1 seq) = xs |> snippet2 (fun r -> r.Id) (fun r -> r.Country) (fun r -> r.Tax)
Тогда ваш переключатель может выглядеть так:
let rawdata csvfile =
match csvfile with
| 1 -> snippetForCsvFile1 CsvFile1
| 2 -> snippetForCsvFile2 CsvFile2
| 3 -> snippetForCsvFile3 CsvFile3
| 4 -> snippetForCsvFile4 CsvFile4
| _ -> failwith "Not a File"
Это тогда должноработайте со своим последним выражением:
let raw =
[ 1 .. 4]
|> List.collect rawdata
У переключателя в вашем вопросе есть проблема, о которой вы не упоминали в своем вопросе:
let rawdata csvfile =
let data =
match csvfile with
| 1 -> CsvFile1
| 2 -> CsvFile2
| 3 -> CsvFile3
| 4 -> CsvFile4
| _ -> failwith "Not a File"
Проблема здесь заключается в том, что привязкаdata
не имеет ни одного типа.Этого можно добиться с помощью объектно-ориентированного подхода с использованием интерфейса (или базового класса), но это не будет особенно идиоматично для F #.Более идиоматическим подходом было бы объявить дискриминационный союз, но, учитывая приведенный вами пример, это не кажется необходимым.