Re[3]: [C#] Таки State Monad
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 11.11.09 12:27
Оценка: 274 (16)
Здравствуйте, gandjustas, Вы писали:

G>Конекретно меня интересует как протягивается этот самый state черезер каскад из лямбда-функций


Если не думать о монадах, то получится примерно следующее.

В ФП функции не могут менять значения, поэтому глобальных переменных нет — вообще нет никаких переменных. А значит нет состояния. Тем не менее, часто надо иметь возможность иметь некое изменяющееся значение.

Например, при проходе по структуре для получения некоего результата на императивном языке мы можем написать что-то вроде:

def sum(list: List of int)
    summa = 0

    for (item in list)
        summa += item

    return summa


Более сложные алгоритмы делают работу с состоянием ещё более запутанным

def foo
    x = 0
    y = bar(x)
    x = zzz(x, y)
    ...


Итак, что характеризует состояние? Важность порядка исполнения и изменяемые переменные. На ФЯ эти свойства эмулируют с помощью так называемых аккумуляторов. Это приём, когда в функцию, помимо необходимых ей аргументов, также передают некую переменную, эмулирующую изменяюмую переменную:

def sum(list: List of int, accum: int)
    if list.empty
        then return accum
        else return sum(tail(list), accum + head(list))


Для сложного алгоритма это выглядит как то так

let (x1,s1) = f0(x0, s0)
    (x2,s2) = f1(x1, s1)
    (x3,s3) = f2(x2, s2)
 in x3


Переменная s1 здесь зависит от того, известна ли s0, а переменная s2 — от того, известна ли s1. Полученная цепочка s0->s1->s2 эмулирует у нас некоторое состояние, относящееся к этому вычислению. xN характеризуют у нас аргументы функций fN, на самом деле они вовсе не должны быть цепочкой, но иногда они зависят друг от друга и, разумеется, состояния.

Допустим теперь, что мы хотим скрыть явное использование переменных sN, потому что алгоритм гораздо симпатичнее запишется в виде

global s

x1 = f0(x0)
x2 = f1(x1)
x3 = f2(x2)

return x3


где функции fN меняют глобальное состояние s. В чистом функциональном языке это можно сделать объединив вычисления с помощью некоего комбинатора. Т.е. сначала мы вычисляем f0(x0), а затем вычисляем f1(x1). Но так как определение x1 у нас отсутствует, то нам придётся завернуть вычисление f1(x1) в лямбду с аргументом x1

f0(x0) THEN (x1 => f1(x1)) THEN (x2 => f2(x2)) THEN (x3 => RETURN x3)


THEN — инфиксный комбинатор двух аргументов. Какой тип он имеет? Вспомним, чем на самом деле являются функции fN (смотрим на код (x1,s1) = f0(x0, s0))

fN :: a -> s -> (r,s)


Т.е. это функция, принимающая некий важный для неё аргумент (или несколько аргументов) и состояние и возвращающая тапл с результатом и состоянием. Я извиняюсь, что написал тип в таком стиле, но привычный алголоподобный стиль будет более коряв.

Рассмотрим выражение f0(x0) THEN (x1 => f1(x1)).

Слева у нас f0(x0). f0 :: a -> s -> (r,s), но так как аргумент мы уже применили, то (a ->) убираем из типа. Получается, что f0(x0) имеет тип s -> (r,s), т.е. это выражение ожидает переменную состояния (тип s), и возвращает тапл с результатом и новым состоянием. Это тип первого аргумента THEN.

Справа у нас x1 => f1(x1). Тип этой лямбды r -> s -> (new_r, s). Здесь стоит тип new_r, т.к. тип результата вычисления может отличаться от типа аргумента. Что должен возвращать x THEN y? Что-то, что можно снова применить к THEN, т.е. что-то с типом (s -> (new_r, s)).

Итак! Полный тип THEN будет выглядеть так

THEN :: (s -> (r,s)) -> (r -> s -> (new_r, s)) -> (s -> (new_r, s))


Тип очень сложный, но в нём можно заметить повторяющийся кусок, который мы выделим в синоним типа (алиас).

type State<s, t> = s -> (t,s)

THEN :: State<s,r> -> (r -> State<s, new_r>) -> State<s, new_r>


Получилось попроще. Мало того, теперь мы видим, что из себя представляет вычисление с состоянием. Это просто функция (!), которая получает состояние и возвращает результат с новым состоянием. Семантически монада состояния очень проста. Функция. Принимает на вход состояние. Возвращает результат и новое состояние.

Итак! Тип для THEN мы получили, теперь попробуем его реализовать. Это как раз несложно. Как видно из его типа (исходного, без использования алиаса) на самом деле THEN имеет 3 аргумента, а не 2. Третий аргумент имеет тип s и это начальное состояние, передаваемое в цепочку вызовов f0->f1.

def THEN(calc1: s => (r,s), calc2: r,s => (new_r,s), state0: s): (new_r, s)
    // у нас две функции и одна нормальная переменная
    // Пока применить эту переменную мы можем только к одной функции
    (res, state1) = calc1(state0)
    // у нас есть аргументы для второй функции
    (new_res, state2) = calc2(res, state1)
    // этот результат мы и возвращаем
    return (new_res, state2)


По сути, это наше let-выражение с явным аккумулятором. С RESULT всё ещё проще. Он принимает некий аргумент, а должен вернуть State<s,a>

def RESULT(val: a, state: s): (a,s)
    // ну что мы ещё можем вернуть?
    return (val, state)


Вот и всё! Теперь мы можем писать так

f0(x0)  THEN (x1 =>
f1(x1)) THEN (x2 =>
f2(x2)) THEN (x3 =>
RETURN x3)


Для более удобной работы с состоянием напишем ещё пару функций.

Получение состояния. Вспомним, что состояние в наших вычислениях на комбинаторе THEN неявное. Чтобы сделать его явным мы должны передать его в результат:

def GET(state: s): (s,s)
    return (state, state)


Изменение состояния. Тут всё просто. Аргумент у нас — новое состояние, его мы и должны вернуть в результирующем состоянии

def PUT(newState: s, state: s): ((),s)
    // забываем про state, у нас есть новое состояние
    return ((), newState)


Тут мы видим, что PUT возвращает () — семантически это отсутствие результата или неважный результат, именно так мы и будем его интерпретировать. Но, т.к. у нас есть такой тип результата, то неплохо было бы иметь близнеца THEN, который не использует результат. Назовём его THEN_

// отличие! calc2 не принимает аргумент типа r
def THEN_(calc1: s => (r,s), calc2: s => (new_r,s), state0: s): (new_r, s)
    // объединяем вычисления, не использую результат первого во втором
    calc = calc1 THEN (_ => calc2)
    // запускаем вычисление с состоянием
    return calc(state0)


Теперь мы можем писать так

def TICK: State<int, ()>
    GET THEN (state =>
    PUT (state + 1))

def TICK3: State<int, ()>
    TICK THEN_
    TICK THEN_
    TICK


Вот и всё. Никаких монад.
Re[4]: [C#] Таки State Monad
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.11.09 16:29
Оценка: +3
Здравствуйте, lomeo, Вы писали:

Елы-палы. Получается без пяти минут статься.

Добавил бы введение и оформил бы в нашем шаблоне. А то ведь через пару недель это сообщение канет в лету и про него все забудут. А так всегда будет на виду и найти можно будет легко.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 11.11.09 07:35
Оценка: 16 (2)
Здравствуйте, Jack128, Вы писали:

J>А ты не мог бы преобразовать labelMonad в код без этой нотации??


Легко
let rec labelMonad = function
  | Leaf(x) ->
    state.Bind(getState, fun s ->
     state.Bind(setState(s+1), fun () ->
      state.Return(Leaf(x,s))))
  | Branch(a,b) ->
    state.Bind(labelMonad a, fun l ->
     state.Bind(labelMonad b, fun r ->
      state.Return(Branch(l,r))))

Обратите внимание, что обращение к s в возврате Leaf(x,s) — это замыкание на параметр внешней лямбды...

В таком виде ещё лучше видно, что состояние проходит все лямбды в бинде.
getState умеет доставать значение состояния и передавать в параметр следующей лямбды, при этом состояние не меняется.
setState может любое значение, на которое можно замкнуться, записать в новое состояние, просто вернув в бинд новое состояние.
Re[16]: Самотест на понимание монад
От: desco США http://v2matveev.blogspot.com
Дата: 11.11.09 20:53
Оценка: 7 (1)
Здравствуйте, desco, Вы писали:

D>можно сделать lift, заточенный под maybe (не уверен, что на F# можно написать обобщенный lift)


поправка, если использовать member constraints, то извернутся можно
type List() = 
    static member Instance = List()
    member x.Return(v) = [v]
    member x.Bind(m, f) = List.collect f m    
let list = List()

type Maybe() = 
    static member Instance = Maybe()
    member x.Return(v) = Some(v)
    member x.Bind(m, f) = match m with | Some(v) -> f v | None -> None
let maybe = Maybe()
    
let inline ret b v = 
    (^b :  (member Return : ^v -> ^mv) (b, v))
    
let inline bind b m f = 
    (^b :  (member Bind : ^v * ^f -> ^mv) (b, m, f)) 

let inline lift2 b f x y =
    bind b x (fun x1 -> 
        bind b y (fun y1 -> ret b (f x1 y1))
    )

// None
let r = maybe {
        let (++) = lift2 maybe (+)
        return! Some(5) ++ None
    }    

// [0;2;1;3]    
let r2 = list {
        let (++) = lift2 list (+)
        return! [0;1] ++ [0;2]
    }
Re[18]: Самотест на понимание монад
От: Пельмешко Россия blog
Дата: 20.11.09 19:37
Оценка: 4 (1)
Здравствуйте, Jack128, Вы писали:

D>>поправка, если использовать member constraints, то извернутся можно

D>>
D>>    (^b :  (member Return : ^v -> ^mv) (b, v)) // А что это такое и где об этом мона почитать???
D>>


Это "Explicit member constraint", поподробнее узнать можно здесь:
http://codebetter.com/blogs/matthew.podwysocki/archive/2009/09/27/generically-constraining-f-part-ii.aspx
[C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.11.09 13:07
Оценка: 2 (1)
Вдохновился вот этим видео и сделал.

Разметка двоичного дерева на C# с использованием State Monad и Linq

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StateMonad
{
    public class Unit
    {
        public static readonly Unit Value = new Unit();

        private Unit() { }
    }

    /// <summary>
    /// State-content pair
    /// </summary>
    /// <typeparam name="S">Type of State</typeparam>
    /// <typeparam name="C">Type of Content</typeparam>
    public class Scp<S,C>
    {
        public Scp(S state, C content)
        {
            State = state;
            Content = content;
        }

        public S State { get; private set; }
        public C Content { get; private set; }

        public override string ToString()
        {
            return "{" + State + ", " + Content + "}";
        }
    }

    /// <summary>
    /// State monad
    /// </summary>
    /// <typeparam name="S">Type of State</typeparam>
    /// <typeparam name="C">Type of Content</typeparam>
    public class StateMonad<S, C>
    {
        public StateMonad(Func<S, Scp<S, C>> s2Scp)
        {
            S2Scp = s2Scp;
        }

        /// <summary>
        /// State to state-content pair
        /// </summary>
        public Func<S,Scp<S,C>> S2Scp { get; private set; }

    }

    /// <summary>
    /// State monad operations
    /// </summary>
    public static class StateMonad
    {
        /// <summary>
        /// "return" monadic operator
        /// </summary>
        /// <typeparam name="S">Type of State</typeparam>
        /// <typeparam name="C">Type of Content</typeparam>
        /// <param name="content">Content</param>
        /// <returns>State monad</returns>
        public static StateMonad<S, C> Return<S, C>(C content)
        {
            return new StateMonad<S, C>(s => new Scp<S, C>(s, content));
        }

        /// <summary>
        /// "return" monadic operator
        /// </summary>
        /// <typeparam name="S">Type of State</typeparam>
        /// <typeparam name="C">Type of Content</typeparam>
        /// <param name="dummy">Dummy argument for type inference</param>
        /// <param name="content">Content</param>
        /// <returns>State monad</returns>
        public static StateMonad<S, C> Return<S, C>(S dummy, C content)
        {
            return new StateMonad<S, C>(s => new Scp<S, C>(s, content));
        }

        /// <summary>
        /// "bind" monadic operator
        /// </summary>
        /// <param name="m">State monad</param>
        /// <param name="selector">Selector function</param>
        /// <returns>State monad</returns>
        public static StateMonad<S, C2> SelectMany<S, C1, C2>(this StateMonad<S, C1> m, Func<C1, StateMonad<S, C2>> selector)
        {
            return new StateMonad<S, C2>(s =>
                {
                    var scp = m.S2Scp(s);
                    return selector(scp.Content).S2Scp(scp.State);
                });
        }

        /// <summary>
        /// "bind" monadic operator
        /// </summary>
        /// <param name="m">State monad</param>
        /// <param name="selector">Selector function</param>
        /// <param name="result">Map result function</param>
        /// <returns>State monad</returns>
        public static StateMonad<S, C2> SelectMany<S, C1, C2, K>(this StateMonad<S, C1> m, Func<C1, StateMonad<S, K>> selector, Func<C1, K, C2> result)
        {
            return m.SelectMany(x => selector(x).SelectMany(y => Return<S,C2>(result(x, y))));
        }

        /// <summary>
        /// Select method for Query Pattern
        /// </summary>
        /// <param name="m">State monad</param>
        /// <param name="selector">Selector function</param>
        /// <returns>State monad</returns>
        public static StateMonad<S, C2> Select<S, C1, C2>(this StateMonad<S, C1> m, Func<C1, C2> selector)
        {
            return new StateMonad<S, C2>(s => 
            {
                var scp = m.S2Scp(s);
                return new Scp<S, C2>(scp.State, selector(scp.Content));
            });
        }

        /// <summary>
        /// State monad "get state" function
        /// </summary>
        /// <typeparam name="S">Type of state</typeparam>
        public static StateMonad<S, S> Get<S>()
        {
            return new StateMonad<S, S>(s => new Scp<S, S>(s, s));
        }

        /// <summary>
        /// State monad "put state" function
        /// </summary>
        /// <typeparam name="S">Type of state</typeparam>
        /// <param name="s">New state</param>
        public static StateMonad<S, Unit> Put<S>(S s)
        {
            return new StateMonad<S, Unit>(_ => new Scp<S, Unit>(s, Unit.Value));
        }
    }

    /// <summary>
    /// Tree constructor
    /// </summary>
    public static class Tr
    {
        /// <summary>
        /// Creates new Leaf
        /// </summary>
        /// <typeparam name="T">Type of content</typeparam>
        /// <param name="content">Content of Leaf</param>
        /// <returns>Binary tree</returns>
        public static Tr<T> Lf<T>(T content)
        {
            return new Lf<T>(content);
        }

        /// <summary>
        /// Creates new Branch
        /// </summary>
        /// <typeparam name="T">Type of content</typeparam>
        /// <param name="left">Left subtree</param>
        /// <param name="right">Right subtree</param>
        /// <returns>Binary tree</returns>
        public static Tr<T> Br<T>(Tr<T> left, Tr<T> right)
        {
            return new Br<T>(left, right);
        }
    }

    /// <summary>
    /// Base class for binary tree node
    /// </summary>
    /// <typeparam name="T">Type of content</typeparam>
    public abstract class Tr<T>
    {
        protected const int Ident = 2;

        /// <summary>
        /// Print node
        /// </summary>
        /// <param name="level"></param>
        public abstract void Show(int level);
    }

    /// <summary>
    /// Branch of binary tree
    /// </summary>
    /// <typeparam name="T">Type of content</typeparam>
    public class Br<T> : Tr<T>
    {
        public Br(Tr<T> left, Tr<T> right)
        {
            Left = left;
            Right = right;
        }

        /// <summary>
        /// Left subtree
        /// </summary>
        public Tr<T> Left { get; private set; }

        /// <summary>
        /// Right subtree
        /// </summary>
        public Tr<T> Right { get; private set; }

        public override void Show(int level)
        {
            Console.Write(new String(' ', level * Ident));
            Console.WriteLine("Branch:");
            Left.Show(level + 1);
            Right.Show(level + 1);
        }
    }

    /// <summary>
    /// Leaf of binary tree
    /// </summary>
    /// <typeparam name="T">Type of content</typeparam>
    public class Lf<T> : Tr<T>
    {
        public Lf(T content)
        {
            Content = content;
        }

        /// <summary>
        /// Content
        /// </summary>
        public T Content { get; private set; }

        public override void Show(int level)
        {
            Console.Write(new String(' ', level * Ident));
            Console.Write("Leaf: ");
            Console.Write(Content);
            Console.WriteLine();
        }
    }

    class Program
    {
        /// <summary>
        /// Monadic tree labeling
        /// </summary>
        /// <typeparam name="T">Type of content</typeparam>
        /// <param name="tree">Binary tree</param>
        /// <returns>State monad with binary tree with label-contents pair</returns>
        static StateMonad<int, Tr<Scp<int,T>>> GetLabelMonad<T>(Tr<T> tree)
        {
            if (tree is Lf<T>)
            {
                var leaf = tree as Lf<T>;
                var m = from s in StateMonad.Get<int>()
                        from _ in StateMonad.Put(s + 1)
                        from r in StateMonad.Return(0 /*dummy arg*/, Tr.Lf(new Scp<int, T>(s, leaf.Content)))
                        select r;
                return m;
            }
            else if(tree is Br<T>)
            {
                var br = tree as Br<T>;
                var m = from l in GetLabelMonad(br.Left)
                        from r in GetLabelMonad(br.Right)
                        from res in StateMonad.Return(0 /*dummy arg*/, Tr.Br(l, r))
                        select res;
                return m;
            }
            else
            {
                throw new Exception("Invalid object type");
            }
        }

        static void Main(string[] args)
        {
            var tree = 
                Tr.Br
                (
                    Tr.Br(
                        Tr.Lf(1),
                        Tr.Br(Tr.Lf(2), Tr.Lf(3))
                         ),
                    Tr.Lf(4)
                );
            tree.Show(0);
            Console.WriteLine();

            var m = GetLabelMonad(tree);

            var res = m.S2Scp(0);
            
            Console.WriteLine(res.State);
            res.Content.Show(0);

            Console.ReadLine();
        }
    }
}


Кто-нибудь может объяснить как оно работает?
Re[3]: [C#] Таки State Monad
От: Mirrorer  
Дата: 10.11.09 15:27
Оценка: 1 (1)
Здравствуйте, gandjustas, Вы писали:

G>Это как раз просто, меня интересует код связывания. Под отладчиком я вижу что и как происходит, но осознать мозгом почему так работает получается у меня с большим трудом.


все не просто а очень просто.
на псевдокоде
using(int counter =0)
{
   foreach(var leaf in tree.Leaves)
   {
      leaf = new Pair<int, Leaf>(counter++, leaf)
   }
}


У нас есть контекст, внешний по отношению алгоритму обработки.
мы можем из него что-то читать и что-то в него записывать.
Его вполне можно было бы заменить на такую штуку
interface IHaveContext<T>
{
     T GetCurrentContext();
     SetCurrentContext(T val);
}


теперь делаем static переменную, реализовывающую этот интерфейс. Это получится конечно уже не сишарп, а черт знает что, но тут главное уловить идею.

    static context:IHaveContext<int>

   foreach(var leaf in tree.Leaves)
   {
      var currentContext = context.GetCurrentConext();
      context.SetCurrentContext(currentContext.Value +1);

      leaf = new Pair<int, Leaf>(currentContext.Value, leaf)
   }


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

Например так

context1 : IHaveContext<bool>
context2 : IHaveContext<bool>
context3 : IHaveContext<bool>

context1 = SomeWierdOperationThatSucceeds();
if(context1)
    context2 = AnotherUgluSuccessOperation();
       if(context2) 
           context3 = AndThisOperaiontFails();
                if(context3)
                    return true;
else
   return false;


Тут явно видно что внешний контекс дает возможность выполнения внутренним. Типа транзакционность.
Такие развесистые ифы имеют мало кайфа в написании и поддержке. Поэтому их обычно выносят в интерфейсы

//простецкий интерфейс.
//Метод принимает на входе значение текущего контекста, функцию которую надо выполнить 
interface Joiner<T> : where T is IHaveContext<T>
{
     IHaveContext<T> Join(IHaveContext<T> currentContext, Action step);
}
// реализация интерфейса. Тоже тупа до безобразия
class ТруЪJoinier<T> : Joiner<T>
{
    IHaveContext<T> Join(currentContext, step)
   {
     if(currentContext == true)
      // намек на то, что функция, может получить текущий контекст
       var res = step(first)
       return res;
     else
       return false as IHaveContext;
   }
}

// А теперь наш код из предыдущего примера в более пригодном для употребления виде
var joiner = new ТруЪJoiner();

// Да, я в курсе, что тут напутано с типами, и количеством параметров. Важна описываемая идея, а не код
joiner.Join(true,SomeWierdOperationThatSucceeds).Join(AnotherUgluSuccessOperation).Join(AndThisOperaiontFails);


Выглядит симпатишнее чем простыня ифов.
Что еще можно сделать на основе этих вещей. В смысле что может быть контекстом. НУ например слой логики, куда мы хотим подключить логирование.
Мы просто дергаем метод ЛОГ у логгера, а он уже сам узнает в каком контексте мы выполняемся, какие настройки логирования(дебаг, релиз) и прочие вещи,
абсолютно не интересные.
А можно сделать те же контексты аля транзакции, но чтобы они работали по фолсу, а не по ТруЪ.
для этого достаточно заменитть
var joiner = new ТруЪJoiner();

на
var joiner = new FalseJoiner();


А можно при парсинге протягивать контекс типа количество ошибко, текущая строка, текущий символ, текущая лексема.

В общем простор для извращений безграничен.

Ну, вот так как-то.
Re: [C#] Таки State Monad
От: Mirrorer  
Дата: 10.11.09 14:18
Оценка: :)
Здравствуйте, gandjustas, Вы писали:

G>Разметка двоичного дерева на C# с использованием State Monad и Linq


G>Кто-нибудь может объяснить как оно работает?

мне кажется работает очень просто. Бегает рекурсивно по дереву, когда добегает до Leaf, инкрементит его на еденичку от текущего значения, сохраненного в глобальной пеерменной^W^W прошу прощения, монаде и создает новую ноду с разметкой.

код после небольшого рефакторинга
комментарии мои
      static StateMonad<int, Node<StateM<int, T>>> GetLabelMonad<T>(Node<T> tree)
        {
            if (tree is Leaf<T>)
            {
                var leaf = tree as Leaf<T>;
    
                var m = from s in StateMonad.Get<int>() // получили текучее значение счетчика листьев из монады
                        from _ in StateMonad.Put(s + 1) // инкрементировали его
                        from r in StateMonad.Return(0,Node.Leaf(new StateM<int, T>(s, leaf.Content))) // в листе ноды теперь нода с индексом
                        select r;
                return m;
            }

            else if (tree is Branch<T>)
            {
//  а эта ветка просто рекурсивный спуск. ничего интересного               
            }
            else
            {
                throw new Exception("Invalid object type");
            }
        }

могу конечно ошибаться в мелочах, но общая идея должна быть такой.
Re[2]: [C#] Таки State Monad
От: Mirrorer  
Дата: 10.11.09 16:31
Оценка: :)
Здравствуйте, lomeo, Вы писали:

L>А что именно непонятно? Как работает State монада или как работает именно эта реализация State монады?


Мда, мне этот вопрос пришел в голову только после того, как я написал пост с примерами.
Ленивое программирование в действии
Re[2]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.11.09 14:48
Оценка:
Здравствуйте, Mirrorer, Вы писали:

M>Здравствуйте, gandjustas, Вы писали:


G>>Разметка двоичного дерева на C# с использованием State Monad и Linq


G>>Кто-нибудь может объяснить как оно работает?

M>мне кажется работает очень просто. Бегает рекурсивно по дереву, когда добегает до Leaf, инкрементит его на еденичку от текущего значения, сохраненного в глобальной пеерменной^W^W прошу прощения, монаде и создает новую ноду с разметкой.

M>могу конечно ошибаться в мелочах, но общая идея должна быть такой.


Это как раз просто, меня интересует код связывания. Под отладчиком я вижу что и как происходит, но осознать мозгом почему так работает получается у меня с большим трудом.
Re[3]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 10.11.09 16:15
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Это как раз просто, меня интересует код связывания. Под отладчиком я вижу что и как происходит, но осознать мозгом почему так работает получается у меня с большим трудом.


Код связывания? Вы про то, как работает bind?

Так же как в Linq каждый SelectMany обязан возвращать IEnumerable<T>, тут все селекторы возвращают StateMonad — некое вычисление, получающее состояние и возвращающее пару — состояние и контент:
G>                var m = from s in StateMonad.Get<int>()
G>                        from _ in StateMonad.Put(s + 1)
G>                        from r in StateMonad.Return(0 /*dummy arg*/, Tr.Lf(new Scp<int, T>(s, leaf.Content)))
G>                        select r;

Каждый SelectMany оборачивает полученный StateMonad в другой StateMonad...
Получается цепочка дёргающих друг друга делегатов, передающих друг другу по цепочке State-content pair.

G>        public static StateMonad<S, C2> SelectMany<S, C1, C2>(this StateMonad<S, C1> m, Func<C1, StateMonad<S, C2>> selector)
G>        {
G>            return new StateMonad<S, C2>(s =>
G>                {
G>                    var scp = m.S2Scp(s);
G>                    return selector(scp.Content).S2Scp(scp.State);
G>                });
G>        }

То есть логика связывания — вернуть такой StateMonad (обёрнутый делегат), который:
при получении State-content пары вызовет вычисление m, передав эту пару, в результате получится новая пара. Потом вызовет селектор, передав контент новой пары, селектор вернёт вторую StateMonad, её вычислит с новой парой и возратит пару.

То есть по сути — объединили StateMonad и (вычисление, возвращающее StateMonad), получив новый StateMonad...
Re: [C#] Таки State Monad
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 10.11.09 16:19
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Кто-нибудь может объяснить как оно работает?


А что именно непонятно? Как работает State монада или как работает именно эта реализация State монады?
Re[2]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.11.09 18:12
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Здравствуйте, gandjustas, Вы писали:


G>>Кто-нибудь может объяснить как оно работает?


L>А что именно непонятно? Как работает State монада или как работает именно эта реализация State монады?


Конекретно меня интересует как протягивается этот самый state черезер каскад из лямбда-функций
Re[4]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.11.09 18:14
Оценка:
Здравствуйте, Mirrorer, Вы писали:

M>Здравствуйте, gandjustas, Вы писали:


M>Ну, вот так как-то.


Да не, я понимаю что такое монады и как работает разметка дерева.
Меня интересует как работает state monad, в частности каким образом протягивается state через каскад лямбда функций.

Объяснение Бекмана из видео не вставило, а после просмотра отладчиком стало еще запутаннее.
Re[5]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 10.11.09 19:24
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Меня интересует как работает state monad, в частности каким образом протягивается state через каскад лямбда функций.


Ну из бинда всё ясно же:
G>            return new StateMonad<S, C2>(s =>
G>                {
G>                    var scp = m.S2Scp(s);
G>                    return selector(scp.Content).S2Scp(scp.State);
G>                });

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

G>Объяснение Бекмана из видео не вставило, а после просмотра отладчиком стало еще запутаннее.


В этом видео самоё клёвое, что проводится параллель между >>= и композицией
Ни в одном туториале по монадам прям явно в это носом не тычут...

p.s. Вам не мешает куча аннотаций типов? На F# всё же намного приятнее такие вещи изучать, computation expressions понятнее выглядят:
open System

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

type StateBuilder() =

     (*  'a -> M<'a>  *)
     member b.Return(a) =
        State (fun s -> a, s)

     (*  M<'a> * ('a -> M<'b>) -> M<'b>  *)
     member b.Bind((State m), g) =
        State (fun s ->
        let (a, s') = m s in
        let (State f') = g a in f' s')


let getState   = State (fun s ->  s, s)
let setState s = State (fun _ -> (), s)

let Execute (State f) s = let x,_ = f s in x

let state = StateBuilder()

(*******************************************)

type Node<'a> =
   | Branch of 'a Node * 'a Node
   | Leaf   of 'a

   member node.Show(ident) =
     String(' ', ident) |> printf "%s"
     match node with
     | Leaf(x)     -> printfn "Leaf: %A" x
     | Branch(a,b) -> printfn "Branch:"
                      a.Show(ident+2)
                      b.Show(ident+2)

let rec labelMonad = function
  | Leaf(x) ->
    state { let! s = getState
            do! setState (s+1)
            return Leaf(x,s) }
  | Branch(a,b) ->
    state { let! l = labelMonad a
            let! r = labelMonad b
            return Branch(l,r) }

let tree =
    Branch(
      Branch(
        Leaf 1,
        Branch(Leaf 2, Leaf 3)),
      Leaf 4);

tree.Show(0)

let m   = labelMonad tree
let res = Execute m 0

res.Show(0)
Re[6]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.11.09 19:35
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Здравствуйте, gandjustas, Вы писали:


G>>Меня интересует как работает state monad, в частности каким образом протягивается state через каскад лямбда функций.


П>Ну из бинда всё ясно же:

П>
G>>            return new StateMonad<S, C2>(s =>
G>>                {
G>>                    var scp = m.S2Scp(s);
G>>                    return selector(scp.Content).S2Scp(scp.State);
G>>                });
П>

П>Состояние уходит в первое вычисление (m), оттуда возвращается новое состояние (в паре), которое уходит во второе вычисление (возвращённое селектором) и возвращается новое состояние (тоже в паре). То есть после композиции биндом получается одно вычисление, получающее стэйт, пропускающее его через два вычисления и возвращающее результирующий стэйт...
Порвало мозг... *Где здесь стреляющийся смайлик?*

G>>Объяснение Бекмана из видео не вставило, а после просмотра отладчиком стало еще запутаннее.


П>В этом видео самоё клёвое, что проводится параллель между >>= и композицией

П>Ни в одном туториале по монадам прям явно в это носом не тычут...
Дык теория категорий.

Кстати я обманул. На написание на C# сповергло видео (2 части) где бекман показывает State monad на цацкеле и C#. Но там без Linq.
http://channel9.msdn.com/shows/Going+Deep/Brian-Beckman-The-Zen-of-Expressing-State-The-State-Monad/
http://channel9.msdn.com/shows/Going+Deep/Brian-Beckman-The-Zen-of-Stateless-State-The-State-Monad-Part-2/

П>p.s. Вам не мешает куча аннотаций типов? На F# всё же намного приятнее такие вещи изучать, computation expressions понятнее выглядят

На F# и проще, и понятнее, и делал уже, но я хотел еще простые монады (Identity, Maybe, State) с помощью Linq сделать.
Re[6]: [C#] Таки State Monad
От: Jack128  
Дата: 10.11.09 19:56
Оценка:
Здравствуйте, Пельмешко, Вы писали:

В упор не могу понять:

П>let setState s = State (fun _ -> (), s)

setState — чистая функция.
П>let rec labelMonad = function
П> | Leaf(x) ->
П> state { let! s = getState
П> do! setState (s+1) // тут мы игнорим значение этой функции.
П> return Leaf(x,s) }
П> | Branch(a,b) ->
П> state { let! l = labelMonad a
П> let! r = labelMonad b
П> return Branch(l,r) }
П>[/ocaml]

так откуда всплывает новое состояние??
ТОже самое в исходном сообщении:


                var m = from s in StateMonad.Get<int>()
                        from _ in StateMonad.Put(s + 1) // игнорируется значение
                        from r in StateMonad.Return(0 /*dummy arg*/, Tr.Lf(new Scp<int, T>(s, leaf.Content)))
                        select r;


где, где блин сохраняется состояние????
Re[7]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 10.11.09 20:16
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, Пельмешко, Вы писали:


J>В упор не могу понять:


П>>let setState s = State (fun _ -> (), s)
П>>let rec labelMonad = function
П>>  | Leaf(x) ->
П>>    state { let! s = getState
П>>            do! setState (s+1) // тут мы игнорим значение этой функции.
П>>            return Leaf(x,s) }
П>>  | Branch(a,b) ->
П>>    state { let! l = labelMonad a
П>>            let! r = labelMonad b
П>>            return Branch(l,r) }
П>>


В F#
do! expr in cexpr

дешугарится в:
b.Bind(expr,(fun () -> «cexpr»))

обратите внимание, значение expr не игнорируется, а уходит в бинд!

J>так откуда всплывает новое состояние??

J>ТОже самое в исходном сообщении:

J>
J>                var m = from s in StateMonad.Get<int>()
J>                        from _ in StateMonad.Put(s + 1) // игнорируется значение
J>                        from r in StateMonad.Return(0 /*dummy arg*/, Tr.Lf(new Scp<int, T>(s, leaf.Content)))
J>                        select r;
J>


J>где, где блин сохраняется состояние????


Состояние проходит через все вычисления закулисами, в бинде, его можно геттером выдрать напоказ и присвоить контенту:
from s in StateMonad.Get<int>()

А потом замкнуться на это значение сеттером (это не само состояние, это выражение передающееся между вычислениями), который может менять значение настоящего состояния.
Re[8]: [C#] Таки State Monad
От: Jack128  
Дата: 10.11.09 20:57
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Здравствуйте, Jack128, Вы писали:

П>В F#
П>
do! expr in cexpr

П>дешугарится в:
П>
b.Bind(expr,(fun () -> «cexpr»))

П>обратите внимание, значение expr не игнорируется, а уходит в бинд!

угу, вроде проясняется что то.
Вопрос по синтаксису, что означает запись state { expr } ?
Re[9]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 10.11.09 21:10
Оценка:
Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, Пельмешко, Вы писали:
П>>Здравствуйте, Jack128, Вы писали:
П>>В F#
П>>
do! expr in cexpr

П>>дешугарится в:
П>>
b.Bind(expr,(fun () -> «cexpr»))

П>>обратите внимание, значение expr не игнорируется, а уходит в бинд!
J>угу, вроде проясняется что то.
J>Вопрос по синтаксису, что означает запись state { expr } ?

Это computation expression, аналог do-нотации в хаскеле
Грубо говоря эта запись означает "преобразовать expr в вызовы методов экземпляра класса-builder'а — state (экземпляр класса StateBuilder)".
Re[10]: [C#] Таки State Monad
От: Jack128  
Дата: 11.11.09 06:48
Оценка:
Здравствуйте, Пельмешко, Вы писали:

J>>Вопрос по синтаксису, что означает запись state { expr } ?


П>Это computation expression, аналог do-нотации в хаскеле

П>Грубо говоря эта запись означает "преобразовать expr в вызовы методов экземпляра класса-builder'а — state (экземпляр класса StateBuilder)".

А ты не мог бы преобразовать labelMonad в код без этой нотации??
Re[12]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.11.09 07:48
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Здравствуйте, Jack128, Вы писали:


J>>А ты не мог бы преобразовать labelMonad в код без этой нотации??


П>Легко

П>
П>let rec labelMonad = function
П>  | Leaf(x) ->
П>    state.Bind(getState, fun s ->
П>     state.Bind(setState(s+1), fun () ->
П>      state.Return(Leaf(x,s))))
П>  | Branch(a,b) ->
П>    state.Bind(labelMonad a, fun l ->
П>     state.Bind(labelMonad b, fun r ->
П>      state.Return(Branch(l,r))))
П>

П>Обратите внимание, что обращение к s в возврате Leaf(x,s) — это замыкание на параметр внешней лямбды...

П>В таком виде ещё лучше видно, что состояние проходит все лямбды в бинде.

П>getState умеет доставать значение состояния и передавать в параметр следующей лямбды, при этом состояние не меняется.
П>setState может любое значение, на которое можно замкнуться, записать в новое состояние, просто вернув в бинд новое состояние.

Отличная идея, надо desugar_ить LINQ в C# варианте и развернуть Get и Put чтобы понятнее было как передается state
Re[12]: [C#] Таки State Monad
От: Jack128  
Дата: 11.11.09 07:57
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Здравствуйте, Jack128, Вы писали:


Во! Терь вроде понятно. нуно тока потренироваться самому в написании монад. Сенкс большой.
Re[13]: [C#] Таки State Monad
От: Пельмешко Россия blog
Дата: 11.11.09 08:08
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Отличная идея, надо desugar_ить LINQ в C# варианте и развернуть Get и Put чтобы понятнее было как передается state


Вот так оно дешугарится, ну почти:
static StateMonad<int, Tr<Scp<int,T>>> GetLabelMonad<T>(Tr<T> tree)
{
    if (tree is Lf<T>)
    {
        var leaf = tree as Lf<T>;
        var m =
            StateMonad.Get<int>().SelectMany(s =>
              StateMonad.Put(s + 1).SelectMany(_ =>
                StateMonad.Return(0, Tr.Lf(new Scp<int, T>(s, leaf.Content)))));

        return m;
    }
    else if(tree is Br<T>)
    {
        var br = tree as Br<T>;

      var m =
          GetLabelMonad(br.Left).SelectMany(l =>
            GetLabelMonad(br.Right).SelectMany(r =>
              StateMonad.Return(0, Tr.Br(l, r))));

        return m;
    }
    else
    {
        throw new Exception("Invalid object type");
    }
}

В этом варианте не нужен Select и ещё одна перегрузка SelectMany, они нужны только из-за особенностей query-синтаксиса...
Re[7]: [C#] Таки State Monad
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 11.11.09 08:43
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>На F# и проще, и понятнее, и делал уже, но я хотел еще простые монады (Identity, Maybe, State) с помощью Linq сделать.


CPS и Maybe монады на LINQ functional-dotnet
Re[13]: Оффтоп, чисто по ф-шарпу вопрос
От: Jack128  
Дата: 11.11.09 08:44
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Пельмешко, Вы писали:


П>>Здравствуйте, Jack128, Вы писали:


J>>>А ты не мог бы преобразовать labelMonad в код без этой нотации??


П>>Легко

П>>
П>>let rec labelMonad = function
П>>  | Leaf(x) ->
П>>    state.Bind(getState, fun s ->
П>>     state.Bind(setState(s+1), fun () ->
П>>      state.Return(Leaf(x,s))))
П>>  | Branch(a,b) ->
П>>    state.Bind(labelMonad a, fun l ->
П>>     state.Bind(labelMonad b, fun r ->
П>>      state.Return(Branch(l,r))))
П>>


Хм. Я правельно понимаю, что функция без параметров и функция, которая принимает unit в F# — совместимы??
Re[14]: Оффтоп, чисто по ф-шарпу вопрос
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.11.09 08:53
Оценка:
Здравствуйте, Jack128, Вы писали:




J>Хм. Я правельно понимаю, что функция без параметров и функция, которая принимает unit в F# — совместимы??


Функция вида
let f () = 1


имеет тип unit -> int

А вот значение uint получит нельзя
Re[14]: Оффтоп, чисто по ф-шарпу вопрос
От: Пельмешко Россия blog
Дата: 11.11.09 08:55
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Хм. Я правильно понимаю, что функция без параметров и функция, которая принимает unit в F# — совместимы??


По сути unit — это и есть параметр.
bind — обобщённая функция, вторым аргументом получает ('a -> M<'b>), а под такой тип подходит и функция (unit -> ...), то есть 'a может быть и unit'ом.
Re[15]: Оффтоп, чисто по ф-шарпу вопрос
От: Пельмешко Россия blog
Дата: 11.11.09 09:01
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>А вот значение uint получит нельзя


Мона, по сути () в параметрах — это всегда истинный паттерн матчинг по значению (которое у юнита всегда одно). С помощью as можно получить значение, с которым сматчился шаблон ():
let f (() as x) = x
Re[15]: Оффтоп, чисто по ф-шарпу вопрос
От: Jack128  
Дата: 11.11.09 09:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Jack128, Вы писали:



G>
G>


J>>Хм. Я правельно понимаю, что функция без параметров и функция, которая принимает unit в F# — совместимы??


G>Функция вида

G>
G>let f () = 1
G>


G>имеет тип unit -> int


G>А вот значение uint получит нельзя


В смысле unit ?? Почему нельзя, вроде компилится:

let unitValue = printf "%A" 10

правда на самом деле unitValue — это null, при попытке вызова unitValue.GetTYpe() — NRE вылетает.
Re[16]: Оффтоп, чисто по ф-шарпу вопрос
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.11.09 09:14
Оценка:
Здравствуйте, Jack128, Вы писали:

J>правда на самом деле unitValue — это null, при попытке вызова unitValue.GetTYpe() — NRE вылетает.

Ну я именно это и имел ввиду
Re[14]: Самотест на понимание монад
От: Jack128  
Дата: 11.11.09 09:22
Оценка:
Здравствуйте, Jack128, Вы писали:

Вот пробую написать простейшую, как понимаю, монаду MayBe


type MayBe<'a> = 
  | Just of 'a
  | None

type MayBeBuilder() = 
     (*  'a -> M<'a>  *)
     member b.Return(a) =
        Just a

     (*  M<'a> * ('a -> M<'b>) -> M<'b>  *)
     member b.Bind(m, g) =
        match m with
            | Just a -> g a
            | None   -> None

let mayBe = MayBeBuilder()

let getNone() = None

let result = mayBe {
    let! tmp = Just 10
    let! noneValue = None
    return tmp + noneValue    
 }
 
printf "%A" result


вроде всё работает. Но с практической точки зрения напрягает, что я должен каждое значение связывать оператором let!

то есть хотелось бы вместо:

let getNone() = None
let getTen() = Just 10

let result = mayBe {
   let! ten = getTen()
   let! none = getNone()
   return ten + none
}



писать:


let result = mayBe {
    return getTen() + getNone()
 }


такое возможно
Re[15]: Самотест на понимание монад
От: desco США http://v2matveev.blogspot.com
Дата: 11.11.09 11:50
Оценка:
<skipped/>

можно сделать lift, заточенный под maybe (не уверен, что на F# можно написать обобщенный lift)
let liftToMaybe f x y = maybe {
    let! vx = x
    let! vy = y
    return f vx vy
    }
    
let res = 
    maybe {
        let (++) = liftToMaybe (+)
        return! get10() ++ get1()
    }
Re[4]: [C#] Таки State Monad
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.11.09 13:31
Оценка:
Здравствуйте, lomeo, Вы писали:

Супер!!!

L>Вот и всё. Никаких монад.

Ага, почти
Re[16]: Самотест на понимание монад
От: Jack128  
Дата: 11.11.09 14:22
Оценка:
Здравствуйте, desco, Вы писали:

Не, ну так понятно. А вот в таком случае например что делать?

type Record1 = { age: int MayBe }
type Record2 = { rec1: Record1 MayBe }


let getRec2() = Just { rec1 = Just { age = Just 10 } }

let age = mayBe {
    let! rec2 = getRec2()
    let! rec1 = rec2.rec1
    let! age = rec1.age
    return age
}
Re[17]: Самотест на понимание монад
От: desco США http://v2matveev.blogspot.com
Дата: 11.11.09 14:44
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, desco, Вы писали:


J>Не, ну так понятно. А вот в таком случае например что делать?


J>
J>type Record1 = { age: int MayBe }
J>type Record2 = { rec1: Record1 MayBe }


J>let getRec2() = Just { rec1 = Just { age = Just 10 } }

J>let age = mayBe {
J>    let! rec2 = getRec2()
J>    let! rec1 = rec2.rec1
J>    let! age = rec1.age
J>    return age
J>}
J>


ничего
Maybe — это способ описать вычислительный процесс, который может завершится неудачно на каком-либо этапе, причем опасные шаги четко обозначены. Здесь это как раз очень хорошо наблюдается.
Re[17]: Самотест на понимание монад
От: Jack128  
Дата: 12.11.09 10:14
Оценка:
Здравствуйте, desco, Вы писали:

D>Здравствуйте, desco, Вы писали:


D>>можно сделать lift, заточенный под maybe (не уверен, что на F# можно написать обобщенный lift)


D>поправка, если использовать member constraints, то извернутся можно

D>
D>type List() = 
D>    static member Instance = List()
D>    member x.Return(v) = [v]
D>    member x.Bind(m, f) = List.collect f m    
D>let list = List()

D>type Maybe() = 
D>    static member Instance = Maybe()
D>    member x.Return(v) = Some(v)
D>    member x.Bind(m, f) = match m with | Some(v) -> f v | None -> None
D>let maybe = Maybe()
    
D>let inline ret b v = 
D>    (^b :  (member Return : ^v -> ^mv) (b, v)) // А что это такое и где об этом мона почитать???
    
D>let inline bind b m f = 
D>    (^b :  (member Bind : ^v * ^f -> ^mv) (b, m, f)) 

D>let inline lift2 b f x y =
D>    bind b x (fun x1 -> 
D>        bind b y (fun y1 -> ret b (f x1 y1))
D>    )

D>// None
D>let r = maybe {
D>        let (++) = lift2 maybe (+)
D>        return! Some(5) ++ None
D>    }    

D>// [0;2;1;3]    
D>let r2 = list {
D>        let (++) = lift2 list (+)
D>        return! [0;1] ++ [0;2]
D>    }    
D>
Re[17]: Самотест на понимание монад
От: dsorokin Россия  
Дата: 14.11.09 17:26
Оценка:
По-моему пример с монадой (workflow) Maybe через чур хаскельный. В F# все таки энергичный порядок вычислений. Это нужно учитывать. Например, в книге Expert F# вводится явная ленивость через лямбду. То есть вместо 'a option внутри монады используется функция unit -> 'a option.
Re[5]: Дваждую статью [−]
От: Qbit86 Кипр
Дата: 20.11.09 16:34
Оценка:
 
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Дваждую статью [−]
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.11.09 16:36
Оценка:
Чё?

 
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Дваждую статью [−]
От: Qbit86 Кипр
Дата: 20.11.09 16:55
Оценка:
Здравствуйте, VladD2, Вы писали:

Q>>Дваждую статью

VD>Чё?

Забей. Просто хотел поддержать твоё предложение, адресованное lomeo, о написании статьи про монаду State.
Глаза у меня добрые, но рубашка — смирительная!
Re[5]: [C#] Таки State Monad
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 20.11.09 16:56
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Елы-палы. Получается без пяти минут статься.


Я в fprog всё никак не допишу
Re[6]: [C#] Таки State Monad
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.11.09 17:48
Оценка:
Здравствуйте, lomeo, Вы писали:

VD>>Елы-палы. Получается без пяти минут статься.


L>Я в fprog всё никак не допишу


Ты уже почти все написал, что нужно.

Добавь введение описывающие смысл вопроса и перепиши примеры на том же C# чтобы большей аудитории было понятно происходящее (можно конечно и без этого) и пришли на submit@rsdn.ru или мне. С редактурой мы поможем, если что...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[19]: Самотест на понимание монад
От: Jack128  
Дата: 05.12.09 19:33
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Здравствуйте, Jack128, Вы писали:


D>>>поправка, если использовать member constraints, то извернутся можно

D>>>
D>>>    (^b :  (member Return : ^v -> ^mv) (b, v)) // А что это такое и где об этом мона почитать???
D>>>


П>Это "Explicit member constraint", поподробнее узнать можно здесь:

П>http://codebetter.com/blogs/matthew.podwysocki/archive/2009/09/27/generically-constraining-f-part-ii.aspx

Вообще замечательный блог. Еще раз спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.