OCaml-функторы, классы типа Haskell и множественный вывод
Хорошо известно, что OCaml имеет параметрический полиморфизм, и это приводит к некоторым ограничениям. Haskell через его классы типов предлагает специальный полиморфизм, который, очевидно, очень удобен в нескольких ситуациях. Также хорошо известно, что система модулей и функторов OCaml позволяет создать своего рода специальный полиморфизм. См. Замечательный недавний ответ Саймона Шина здесь, например.
Моя точка зрения заключается в том, что в Haskell можно создавать типы, которые выводят классы типов. Например:
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Eq, Show, Read)
Это очень удобно для определения типов, имеющих несколько функций (позволяющих значения типа Person
поддерживать тесты равенства, быть пригодными для печати и читаться в данном примере).
Мой вопрос заключается в следующем: можем ли мы сделать то же самое, просто в OCaml? Просто я имею в виду с основным синтаксисом языка и без многих уловок.
Чтобы привести несколько конкретный пример, предположим, что у нас есть две сигнатуры OCaml
module type Showable = sig
type t
val to_string : t -> string
end
module type Readable = sig
type t
val from_string : string -> t
end
Цель состоит в том, чтобы написать функтор F
, параметризованный модулем, который реализует как Showable
, так и Readable
.
Ответы
Ответ 1
Конечно, на самом деле это довольно простое использование модуля.
module type S = sig
include Showable
include Readable with type t := t (* destructive substitution *)
end
module F ( M : S ) = struct
let fake_id x = M.from_string @@ M.to_string x
end
Деструктивная замена объясняется в руководстве: http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec234
Остальное - обычный материал модуля (см. Руководство для полного объяснения)
Некоторые библиотеки, несущие функтор, очень много полагаются на такой структурный подтипинг. Например, каждый функтор в ocamlgraph определяет свой собственный аргумент типа модуля. Вот пример с модулем Kruskal. Функтор ожидает модуль типа Kruskal.G
, который реализует подзадачу Sig.G
(которая реализуется большинством модулей графа).