Почему алгоритм вывода типов сбивает с толку из-за «Fun.flip Option.bind»? - PullRequest
1 голос
/ 08 мая 2020

Обычная сигнатура объявлений функций в модуле - это когда последний параметр имеет тип основного состояния (Module.t). Как в модуле «Список». Эта форма открывает возможность использования оператора '|>', например:

[1;2;3] |> List.filter ((>)2)
        |> List.map ((-)1)
        |> List.fold_left 0 (+)

Но функция 'bind' в модуле 'Option' не следует этой форме. Он имеет параметр «Option.t» в качестве первого

val bind : 'a option -> ('a -> 'b option) -> 'b option

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

let opt_bind = Fun.flip Option.bind

Но эта не работает. И следующий код был скомпилирован со следующей ошибкой

type a = A of int
type b = B of int 

let f x = Some (A x)
let g (A x) = Some (B x)  
let opt_bind = Fun.flip Option.bind 

let result = 
  (Some 42) |> opt_bind f
            |> opt_bind g
         |> opt_bind g
                     ^                     

Ошибка: это выражение имеет параметр типа a -> b, но ожидалось выражение> типа int -> параметр. Тип a несовместим с типом int

Такая же ситуация с

let result = 
  let x = opt_bind f (Some 42) in
  let x = opt_bind g x in
  x 

Даже после того, как я заметил все типы, у меня все еще есть та же проблема.

let f : int -> a option = fun x -> Some (A x)
let g : a -> b option = fun (A x) -> Some (B x)  
let opt_bind : ('a -> 'b option) -> 'a option -> 'b option = 
  Fun.flip Option.bind 

let result : b option = 
  let x : a option = opt_bind f (Some 42) in
  let x : b option = opt_bind g x in
  x ;;

Но

let result = 
  let x = Option.bind (Some 42) f in
  let x = Option.bind x g in
  x 

работает нормально.

Почему 'opt_bind' имеет неправильное ожидание типа для 'g', как будто 'opt_bind' не является универсальным c?
Как использовать 'bind' с нотацией '|>'?

1 Ответ

4 голосов
/ 08 мая 2020

Ваша проблема в том, что ваше определение opt_bind недостаточно полиморфно c. Поскольку вы определяете его как приложение (от Fun.flip до Option.bind), его нельзя сделать полиморфным c из-за ограничения значений.

Если вы определите его так:

let opt_bind a b = Fun.flip Option.bind a b

или, что то же самое:

let opt_bind a b = Option.bind b a

, тогда все будет работать.

Если вы спросите тип вашего определения opt_bind, вы увидите проблема:

# let opt_bind = Fun.flip Option.bind;;
val opt_bind :
  ('_weak3 -> '_weak4 option) -> '_weak3 option ->
  '_weak4 option = <fun>

Переменные «слабого» типа сообщают вам, что результирующая функция не является полиморфной c.

Существенное отличие состоит в том, что Fun.flip Option.bind синтаксически является приложением ( вызов функции). Такое выражение нельзя сделать полиморфным c. В двух альтернативных формах bind_opt определяется как лямбда (значение функции), которое является синтаксисом c «значение» в терминологии ограничения значения.

Ограничение значения требуется, чтобы убедиться, что polymorphi c функции являются надежными (т. е. не допускают несоответствующих операций со значениями).

Мой выбор для ограничения значений (особенно реализованный в OCaml) - это этот документ: Ослабление ценностных ограничений, Жак Гарриг

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