#polymorphism #ocaml #monads #state-monad #existential-type
#полиморфизм #ocaml #монады #монада состояния #экзистенциальный тип
Вопрос:
Я пытался реализовать монаду состояния в OCaml (в качестве упражнения). Моя реализация выглядит следующим образом:
module type MONAD_BUILDER =
sig
type 'a t
val return : 'a -> 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
end;;
module MonadBuilder = functor (M: MONAD_BUILDER) ->
struct
let ( >>= ) = M.bind
let return = M.return
end;;
module StateM =
struct
type 'a state = { state: 's . 's -> ('a * 's) }
type 'a t = 'a state
let return x = { state = fun s -> (x, s) }
let bind m f =
let aux s =
let (x, s') = m.state s in
(f x).state s'
in { state = aux }
let run m x = fst (m.state x)
end;;
Я выбрал экзистенциальный тип для поля записи, поскольку мне не нравится идея использования функтора и переноса типа состояния в модуль. Вышеупомянутая реализация работает, но я
столкнулся с проблемой при реализации getState
и setState
. Я попытался реализовать их следующим образом:
let getState = { state = fun s -> (s, s) }
let setState s = { state = fun _ -> ((), s) }
Это не работает, поскольку предполагаемые типы полей, например, 'a -> ('a * 'a)
и 'a -> (unit * 'a)
, являются менее универсальными, чем объявленный тип 's . 's -> ('a * 's)
. Я понимаю, почему это происходит, но мне было интересно, есть ли другой способ заставить это работать, используя подход записи?
Спасибо.
Приветствую, Алекс
Ответ №1:
's. 's -> ('a * 's)
является универсальным типом. Вам будет сложно реализовать состояние с универсальными типами…
Нет чистого способа инкапсулировать туда экзистенциальный тип без использования модуля (потому что абстрактные типы предназначены для экзистенциальных типов).
Вы могли бы, конечно, опубликовать тип состояния вместо:
type ('a,'s) state = { state : 's -> ('a * 's) }
Или даже короче,
type ('a,'s) state = 's -> 'a * 's
С дополнительным параметром ваш код выполняется. Однако вы сталкиваетесь с тем фактом, что ваш параметр должен обрабатываться монадой. Итак, вы можете либо скрыть это при создании монады:
module Monad = MonadBuilder(struct
include StateM
type 'a t = ('a,myStateType) state
end)
Или измените дизайн вашей монады, чтобы включить дополнительный параметр типа, который будет использоваться для существующих типов:
module type MONAD_BUILDER =
sig
type ('a,'param) t
val return : 'a -> ('a,'param) t
val bind : ('a,'param) t -> ('a -> ('b,'param) t) -> ('b,'param) t
end;;