2015-11-02 3 views
2

Если ответ на этот вопрос: «вы все это делаете неправильно», непременно сообщите мне более подходящий способ. У меня был код, который был структурирован так:Попытка получить связывание, работающее для сочетания государственной и отложенной монады

type Res<'T> = 
    | E of (DC -> 'T) 
    | V of 'T 

Теперь этот тип в основном используется непосредственно, с большим количеством встроенного кода, который сделал руководство по случаю связывания, который много шаблонного Я пытаюсь избавиться из, поэтому я решил, что превращаю его в выражение вычисления. Было не так сложно получить map, bind и применить право.

Состояние было перенесено через округ Колумбия, но это было громоздким, чтобы получить право, поэтому я изменил его на следующее, что является общей подписью, которую я вижу в обсуждениях с государственной монадой.

type Res<'T> = 
    | E of (DC -> DC * 'T) 
    | V of 'T 

Тогда я решил взять его на следующий уровень и ввести Delayed монады (или В конце концов, или независимо от его имени). Таким образом, я изменил свой тип следующим образом, что делает его рекурсивным:

type Res<'T> = 
    | E of (DC -> DC * Res<'T>) 
    | V of 'T 

Я попробовал несколько вариантов ниже, но постоянно получаю:

Результирующий тип будет бесконечным, когда объединяющим «» а»и 'Res <' а> -> Res < 'б>'

(обычно случается, когда я не призываю возвращения, то есть ниже Res.E <|)

Или я не могу показаться, чтобы получить свои типы права, следующие (по понятным причинам) вызывает ошибку типа, но у меня слепое пятно фиксируя его:

Это выражение, как ожидается, иметь тип 'Res <' a> ', но здесь есть тип' DC * Res < 'a>'.

let rec bind k res = 
    match res with 
    | Res.V v -> k v  // not delayed, should it be? 
    | Res.E e -> 
     Res.E <| fun dc -> bind k (e dc) // here's my error, on 'e dc' 
     //(fun dc -> dc, bind f (e dc)) 
bind k res 

Я понимаю, что подпись должна быть ('a -> Res<'b>) -> Res<'a> -> XRes<'b>. Проблема заключается в том, что рекурсия верна, по крайней мере, в моей голове. Я даже не уверен, почему я подаю результат на Res.E, так как в моем понимании, продолжение k должно уже возвращать этот тип в любом случае.

Кроме того, я в настоящее время имею возвращения следующим образом (после Steve Horsfield):

let result v = Res.V v 

Но также заметили, в некоторых должностях (особенно hilarious Frankenfunctor) это:

let result v = Res.E <| fun dc -> dc, Res.V v 

Может быть, я следуя шаблону, которого я не должен был иметь, но в то время это казалось хорошей идеей, особенно в попытке реорганизовать zillions шаблона кода и заменить его на вычисления, но, возможно, мне следовало бы ick к прямой оценке, это подходило мне в голову;).

Но я чувствую, что я рядом ... Любые идеи?

+0

В Haskell, когда вы хотите применить две монады одновременно, используется Monad Transformers. Я не уверен, что они доступны в F # (или если они даже реализованы - я сам не реализовал). Чтобы получить представление о концепции, я слышал статью [Monad Transformers Step by Step] (https://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf). – paul

+0

@paul, спасибо, я посмотрю. Но почему-то слишком сложно попросить одномерную реализацию для сохранения состояния __nd_ с его задержкой-оценкой. – Abel

+0

есть проект github, который делает некоторые из них (я не могу найти его прямо сейчас, но я думаю, что он использует [FsControl] (https://github.com/gmpl/FsControl) - я уверен, что кто-то помнит его – Carsten

ответ

1

Я не совсем уверен, что смысл этой монады будет, но
Хорошо, подумав об этом немного и чтения заголовка на вопрос в: -), я сделать понять, что это значит, и я вижу, как построить для него bind.

Ваша ошибка в том, что функция, завернутая в Res.E, должна возвращать кортеж, но вы возвращаете его только Res<'b> путем рекурсивного вызова bind. И чтобы отразить это, вы передаете результат e в рекурсивный bind вызов вместо Res<'b> параметра, а e фактически возвращает кортеж.

Чтобы сделать это правильно, вам нужно destructure результата e, а затем передать вторую часть рекурсивных bind вызова и пару его результата с первой частью:

let rec bind (k: 'a -> 'b Res) (res: 'a Res) : 'b Res = 
    match res with 
    | V a -> k a 
    | E e -> 
     E <| fun dc -> 
     let dc', res' = e dc 
     dc', bind k res' 

Терминала дела, я не» Полагаю, что также следует отложить. То есть, я не вижу причины для этого. Насколько я понимаю, интерпретация дела V x должна быть «», вычисление, которое не изменяет состояние и возвращает x », и в этом случае его задержка не добавляет ничего, кроме дополнительного лямбда-выражения.

+0

Спасибо , вы правы, это работает! Я не был (de- tupling;). Что касается его значения, вложенный тип I я использую из-за совета здесь при построении Delayed monad (с аргументом unit). Добавленный аргумент 'dc' и в кортеже - это состояние, которое помечено вместе и вытолкнуто/нажато при каждом выполнении. Из этого следует (если я делаю это правильно) образец государственной монады. Все это сочетание двух. – Abel

+0

Btw, я не совсем уверен, что он теперь все еще хвост-рекурсивный, потому что recerve-call 'bind' теперь зависит от результата' e dc'. Но поскольку это само по себе не является рекурсивным, я могу быть в безопасности (хотя глубина здесь не смехотворна, в конце концов это может не иметь значения). – Abel

+0

Во-первых, я отредактировал ответ. Во-вторых, я не думаю, что вы можете когда-либо сделать это хвостовым рекурсивом в соответствии с этим определением, но хорошая новость заключается в том, что эта вещь на самом деле вообще не рекурсивна, если вы думаете об этом. Несмотря на то, что он вызывает 'bind' в определении' bind', этот вызов не является действительно «рекурсивным» в том смысле, что 'bind' не называет себя частью своей собственной оценки. Вместо этого он возвращает _closure_, который снова вызывает 'bind'. –