Монада состояния, почему не кортеж?

Я только что перенес голову вокруг монад (по крайней мере, я хотел бы думать, что я имею), и более конкретно монада состояния, который некоторые люди, которые являются путем, более умным затем меня вычисленный, таким образом, я - вероятно, способ с этим вопросом.

Так или иначе монада состояния обычно реализуется с M <'a> как что-то вроде этого (F#):

type State<'a, 'state> = State of ('state -> 'a * 'state)

Теперь мой вопрос: Есть ли какая-либо причина, почему Вы не могли использовать кортеж здесь? Другой затем возможная неоднозначность между MonadA<'a, 'b> и MonadB<'a, 'b> который оба стал бы эквивалентом ('a * 'b) кортеж.

Править: Добавленный пример для ясности

type StateMonad() =
  member m.Return a = (fun s -> a, s)
  member m.Bind(x, f) = (fun s -> let a, s_ = x s in f a s_)

let state = new StateMonad()
let getState = (fun s -> s, s)
let setState s = (fun _ -> (), s) 
let execute m s = m s |> fst
9
задан thr 7 April 2010 в 20:46
поделиться

3 ответа

Монада состояния по существу работает с типом 'state ->' res * 'state , который представляет вычисление, которое принимает некоторое начальное состояние и производит результат (вместе с новым значением состояния).

Если вы спрашиваете, имеет ли значение, даем ли мы какое-то особое имя этому типу (например, State <'state,' res> ), то ответ - это не имеет значения. Единственная цель присвоения типу специального имени - это сделать код более читабельным. Например, давайте рассмотрим две возможные сигнатуры типа в следующем примере:

let foo n = state {
  let! m = getState()
  do! setState(m + 1)
  return sprintf "Result: %d" (n * m) }

// Using State<'state, 'res> type:
val foo : int -> State<int, string>

// Using the underlying representation:
val foo : int -> int -> int * state

Первая сигнатура типа более четко говорит о том, что мы пишем функцию в некоторой монаде. Второй пример - это просто функция, которая принимает два значения int . Я думаю, что главное преимущество первого состоит в том, что вам легче понять, что тип может использоваться из других монадических вычислений (написанных с использованием состояния {...} ).

Однако, как я уже отмечал, это не техническое требование. Люди, вероятно, используют этот стиль, потому что многие монады пришли из Haskell, где монады связаны с типом (например, State <'state,' res> ), а не с построитель вычислений (например, state ), поэтому кажется хорошей идеей определить новый тип для каждой монады в Haskell.

12
ответ дан 4 December 2019 в 09:12
поделиться

Тип монадического значения в вашем примере - это не просто кортеж - это функция, возвращающая кортеж:

'state -> 'res * 'state

Если вы спрашиваете, действительно ли вы можете использовать просто 'state *' res в качестве типа монадических вычислений, тогда ответ будет отрицательным. Это не сработает, потому что нет способа (безопасно) реализовать операцию возврата, которая должна иметь следующую сигнатуру типа:

// how would we get a value of type 'state in the implementation?
val return : 'a -> 'state * 'a
6
ответ дан 4 December 2019 в 09:12
поделиться

Ах, да, если возникает вопрос: следует ли мне использовать одинарный тег Discriminated Union, который несет значение данных типа T, или я должен просто использовать T, тогда вы можете использовать любой из них.

В Haskell вам необходимо использовать тег данных с монадами, поскольку синтаксис Haskell do определяет тип монады на основе типов значений (представление кортежа может быть экземпляром не более одной монады) . В то время как в F # выражения вычислений явно относятся к типу монады (например, состояние {...} или async {...} или что-то еще), поэтому в этом ограничении нет необходимости, один и тот же тип представления может использоваться для нескольких монад.

5
ответ дан 4 December 2019 в 09:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: