Удивительный (для меня) тип простой функции F #

#types #f#

#типы #f#

Вопрос:

Я пишу функцию F # следующим образом, чтобы вычислить произведение списка чисел с суммой «r».

 > let rec proda xs r =
-     match xs with
-     | [] -> r
-     | x::xr -> proda xr (x*r)
- ;;
  

К моему удивлению, его тип proda , согласно консоли F #:

 val proda : xs:int list -> r:int -> int
  

Почему тип не proda

 val proda : xs:a' list -> r:a' -> a'
  

Ответ №1:

Предполагается int , что тип аргумента равен, потому что вы используете * оператор для объединения элементов списка x * r . Это определяет тип x и r как int и, следовательно, xs станет int list .

Если вы хотите избежать этого, вы могли * бы заменить функцию, указанную в качестве дополнительного аргумента:

 let rec proda f xs r =
  match xs with
  | [] -> r
  | x::xr -> proda f xr (f x r) 
  

Это имеет следующий тип:

 val proda : f:('a -> 'b -> 'b) -> xs:'a list -> r:'b -> 'b
  

Это очень полезная функция, и она включена в основную библиотеку F # как List.fold (с переключением последних двух аргументов)! Это несколько более общий, потому что результат может быть другого типа, чем элементы списка ( a против b ), но это простое обобщение того, что у вас есть.

РЕДАКТИРОВАТЬ Вы правы, с которым * также можно использовать float . Чтобы поддержать это, вам нужно будет создать функцию inline . Это немного сложно, потому что вы не можете сделать это с помощью рекурсивной функции, поэтому вам нужно добавить вложенную рекурсивную функцию:

 let inline proda xs r =
  let rec loop xs r = 
    match xs with
    | [] -> r
    | x::xr -> loop xr (x*r) 
  loop xs r
  

С помощью встроенных функций компилятор может вывести «ограничения статического члена», что, по сути, является способом сказать «любой тип, который поддерживает * «. Синтаксис немного затянутый:

 val inline proda :
  xs: ^a list -> r: ^b ->  ^b
    when ( ^a or  ^b) : (static member ( * ) :  ^a *  ^b ->  ^b)
  

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

1. Спасибо. Тем не менее, «*» не ограничивается целыми числами. Это и для поплавков тоже! Например, «fun x-> x * 2.8» — это функция типа float-> float .

2. Только что обнаружил, что «fun x y -> x * y» имеет тип «int -> int -> int». Удивлен.

3. Вы правы — вы можете * работать как с плавающими, так и с целыми inline числами. См. Редактирование.