F# Совпадение-это неудачное сравнение типов между «списком T» и «списком a -> «списком b»

#.net #f#

Вопрос:

У меня есть следующая очень простая функция в f#:

 let private addOnsFromDto (addOns: MyType1 []) =
        let typedList  = List.empty<MyType2>
        match addOns with
            | null -> typedList
            | _ -> List.map<MyType1 , MyType2> (fun a -> MyType2.create a.Prop1 a.Prop2) 
 

Компилятор f# жалуется на это сообщение об ошибке:

 All branches of a pattern match expression must return values of the same type as the first branch, which here is 'MyType2 list'. This branch returns a value of type 'MyType1 list -> MyType2 list'.
 

Я понимаю, что в первой ветке у меня есть «список MyType2». Я понимаю, что во второй ветви у меня есть функция, которая сопоставляет список со списком «список MyType2», поэтому имеет смысл, что компилятор жалуется на несогласованные типы в двух ветвях.

Однако, учитывая, что выходные данные из второй ветви того же типа, что и выходные данные из первой ветви, я бы ожидал, что компилятор будет в порядке. Нужно ли мне что-то делать, чтобы «принудительно» выполнить оценку функции List.map, чтобы эти два типа были эквивалентны? Если да, то как я могу этого достичь?

Решение

Для всех, кто считает это полезным, решение, как объясняется в полезных ответах, заключается в том, что мне нужно было передать исходный список в функцию карты, чтобы были предоставлены оба параметра.

 let private addOnsFromDto (addOns: MyType1 []) =
        let typedList  = List.empty<MyType2>
        match addOns with
            | null -> typedList
            | _ -> addOns 
                |> Array.toList 
                |> List.map<MyType1 , MyType2> (fun a -> MyType2.create a.Prop1 a.Prop2) 
 

Комментарии:

1. Нет, выходные данные не совпадают: первая ветвь возвращает список, а вторая ветвь возвращает функцию. Не список, а функция. Вот подсказка: List.map требуется два параметра, а не один.

Ответ №1:

List.map требуется два аргумента, но вы привели только один. Вам нужно добавить список входных данных в качестве аргумента:

 List.map<MyType1 , MyType2> (fun a -> MyType2.create a.Prop1 a.Prop2) addOns
 

Или, более идиоматично:

 addOns |> List.map (fun a -> MyType2.create a.Prop1 a.Prop2)
 

Комментарии:

1. Спасибо. Я неправильно понял, что | _ -> синтаксис в инструкции match передавал переменную addOns в функцию map!

Ответ №2:

Сообщение об ошибке This branch returns a value of type 'MyType1 list -> MyType2 list'. на самом деле говорит вам, что здесь отсутствует параметр. Вот как работает частичное приложение. Если вы не укажете достаточное количество параметров, вы обычно получите сообщение, подобное этому, с типом со стрелкой в нем. Отсутствующим параметром являются данные addOns типа MyType1 list , поэтому аддоны необходимо сначала преобразовать из MyType1 array MyType1 list в.

Вот к чему я пришел. Я включил фиктивные типы, так что это компилируется само по себе. Также удалены аннотации типов, чтобы облегчить чтение. Когда вы задаете вопросы, хорошо, если вы можете предоставить код, который компилируется.

 type MyType1 = { Prop1: int; Prop2: float }
type MyType2 = { Prop1: int; Prop2: float }
    with
        static member create p1 p2 = { MyType2.Prop1 = p1; Prop2 = p2 }

let private addOnsFromDto (addOns: MyType1 array) =
    match addOns with
    | null -> []
    | addOns ->
        addOns
        |> Array.toList
        |> List.map (fun a -> MyType2.create a.Prop1 a.Prop2)
 

Обновление, Дополнительное примечание: В последнем случае совпадения я использовал теневое объявление, addOns а не подчеркивание. В этом случае, возможно, не имеет большого значения, сделано ли это таким образом или с подчеркиванием, хотя я предпочитаю этот способ по причинам — возможно, это имеет некоторое значение для уменьшения возможных ошибок во время рефакторинга и для ясности — но в других случаях у вас нет доступа к значению, не делая этого таким образом, поэтому вы должны. Со ссылкой на ваш комментарий в другом ответе; подчеркивание-это всего лишь способ сообщить компилятору, что вам не нужно использовать значение, поэтому вам не нужно его имя.

Комментарии:

1. Спасибо — Объяснение того, что этот тип ошибки обычно связан с частичным применением, чрезвычайно полезно. Я видел это несколько раз раньше, но не думаю, что понимал это раньше. Я буду знать, где искать в следующий раз!