Да и нет.Тип, который зависит от значения времени выполнения, называется зависимым типом , и OCaml не поддерживает зависимые типы в полной мере.Обычные языки программирования не поддерживают их, поскольку они делают программирование довольно громоздким.Например, теоретически, у OCaml достаточно зависимой типизации для поддержки вашего случая, за исключением того, что вы не сможете использовать list
или int
, и вам придется использовать GADT-представление для них.И в конце концов, это вряд ли будет полезно.Потому что система типов OCaml все еще статична, поэтому она должна проверить, что ваша программа действительна для всех возможных наборов перед ее выполнением.Это сильно ограничит набор типизированных программ.
Однако, используя абстрактные типы в сочетании с фантомными типами, можно произвольно кодировать инварианты и полагаться на систему типов для ее сохранения.Хитрость заключается в том, чтобы определить небольшое доверенное ядро, где инвариант вводится вручную.
Используя ваш первый пример,
module Foo : sig
type t
val create : int list -> int list -> int list -> (t,error) result
end = struct
type t = {all_nums: int list; nums1: int list; nums2: int list;}
type error = Broken_invariant
let create all_nums nums1 nums2 =
if invariant_satisfied all_nums nums1 nums 2
then Ok {all_nums; nums1; nums2}
else Error Broken_invariant
end
Используя это запечатанное представление, невозможно создать вне модуля Foo
значение типа Foo.t
, для которого invariant_satisfied
не true
.Следовательно, ваше Foo
- это доверенное ядро - единственное место, где вам нужно проверить, сохраняется ли инвариант.Система типов позаботится обо всем остальном.
Вы можете кодировать гораздо более сложные инварианты и быть более выразительными, если будете использовать фантомные типы, например,
module Number : sig
type 'a t
type nat
type neg
type any = (nat t, neg t) Either.t
val zero : nat t
val one : nat t
val of_int : int -> any
val padd : nat t -> nat t -> nat t
val gadd : 'a t -> 'b t -> any
end = struct
type 'a t = int
type nat
type neg
type any = (nat t, neg t) Either.t
let zero = 0
let one = 1
let of_int x = if x < 0 then Right x else Left x
let padd x y = x + y (* [see note 2] *)
let gadd x y = of_int (x + y)
end
, где тип Either.t
определен как type ('a,'b) t = Left of 'a | Right of 'b
Примечания
Примечание 1. Ваш первый пример может быть закодирован таким образом, что невозможно разбить инвариант, например, вместо дублирования ваших данных в all_nums
, вы можетепредставьте ваши типы {nums1 : int list; nums2 : int list}
и определите all_nums
как функцию, let all_nums = List.append
Примечание 2. Фактически, поскольку OCaml, как и многие другие языки программирования, использует модульную арифметику, добавление двух положительных чисел можетпривести к отрицательному числу, поэтому наш пример не выполняется.Но для примера давайте проигнорируем это:)