Как читать подписи типа F #? - PullRequest
13 голосов
/ 24 июня 2011

Я борюсь с нотацией подписи типа F #.Например, допустим, у вас есть функция Fold:

let rec Fold combine acc l =
...

, которая может иметь подпись этого типа:

('a -> 'b -> 'a) -> 'a -> list<'b> -> 'a

, которую я бы прочитал как

aфункция, которая имеет три аргумента:

  • функция, которая принимает 'a, a' b и возвращает a '
  • an'a
  • список' b

и возвращает 'a.

Нотогда для моего мозга пещерных людей было бы более разумно выразить это как

('a, 'b -> 'a), 'a, list<'b> -> 'a

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

Может кто-нибудь, пожалуйста, просветить меня?

Ответы [ 4 ]

15 голосов
/ 24 июня 2011

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

Вы читаете, первая функция верна. Для мгновенного дешифрования подписи типов выражаются так:

val functionName = inputType1 -> inputType2 -> ... -> inputTypeN -> returnType

Как правило, обозначение стрелки указывает на то, что функция может быть карри.

// val add4 : int -> int -> int -> int -> int
let add4 a b c d = a + b + c + d;;

// val f : (int -> int)
let f = add4 1 2 3 // returns (int -> int) waiting for last argument

Поскольку функция является карри, технически вы можете написать ее так:

// val add4 : int -> int -> int -> int -> int
let add4 = (fun a -> (fun b -> (fun c -> (fun d -> a + b + c + d))));;

// val f : (int -> int)
let f = fun x -> add4 1 2 3 x

Если подумать, подпись add4 эквивалентна этой:

val add4 : int -> (int -> (int -> (int -> int) ) )

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

6 голосов
/ 24 июня 2011

Подписи написаны таким образом из-за того, что называется Curry .Несколько более точный способ описания вашей функции состоит в том, что она принимает (функция, которая принимает 'a и возвращает функцию от 'b до 'a) и возвращает функцию, которая принимает 'a и возвращаетфункция от list<'b> до 'a.Из-за этого подпись типа может быть переписана как

('a -> 'b -> 'a) -> ('a -> (list<'b> -> 'a))
5 голосов
/ 24 июня 2011

Вы можете написать аналогичную функцию в F #, которая имеет тип, который вы предлагаете (но в F # она будет записана как ('a * 'b -> 'a) * 'a * list<'b> -> 'a. Однако преимущество существующей функции заключается в том, что ее легко частично применить,только предоставляя префикс аргументов. Например:

let sum = List.fold (+) 0

Используя ваше определение, вы должны будете написать

let sum l = List.fold((fun (x,y) -> x + y), 0, l)
2 голосов
/ 24 июня 2011

Причина в том, что в функциональном программировании каждая функция на самом деле имеет только один параметр.

Итак, предположим, что у вас есть функция с именем Sum как:

int -> int -> int

Это занимает 2 int ивернуть один int.Теперь, если вы вызовете эту функцию, просто передав одну int, вы не получите никакой ошибки компилятора, скорее, возвращаемое значение будет иметь тип int -> int.Итак, вы видите, что обозначение стрелки соответствует этому поведению.Такое поведение известно как карри.Проверить: http://en.wikipedia.org/wiki/Currying

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...