C # Продолжение Монады Реализация

1

Я работаю над возможностью цепочки функций. Я создал класс под названием continuationmonad, который принимает значение, и функцию из a => b. Это позволяет мне использовать fmap и bind, чтобы связать их вместе. Я также использовал ленивые разрешения на отсрочку, если это возможно.

Действительно ли этот класс является продолжением монады или это что-то еще. Мне трудно найти хорошую литературу, которая не является Haskell.

Также любые комментарии о том, как улучшить/исправить это.

using NUnit.Framework;
using System;

namespace Monads
{

    public class Continuation<Input, Output>{
        public Continuation(Input value, Func<Input,Output> function){
            this.value = new Lazy<Input>( () => value);
            this.function = function;
        }

        public Continuation(Lazy<Input> value, Func<Input,Output> function){
            this.value = value;
            this.function = function;
        }

        public Continuation<Output, Result> FMap<Result>(Func<Output, Result> map){
            return new Continuation<Output, Result>(new Lazy<Output>( () => Run() ), x => map(x));
        }

        public Continuation<Output,Result> Bind<Result>(Func<Output, Continuation<Output, Result>> f){
            return f(Run());
        }

        public Output Run(){
            return function(value.Value);
        }

        private Func<Input, Output> function;
        private Lazy<Input> value;
    }

    public static class ContinuationExtension{
        public static Continuation<A,B> Unit<A,B>(this Func<A,B> f, A value){
            return new Continuation<A, B>(value,f);
        }

        public static Continuation<A,B> Unit<A,B>(this A value,Func<A,B> f){
            return new Continuation<A, B>(value,f);
        }
    }

    [TestFixture]
    public class MonadTests
    {

        public Continuation<int,int> Wrapped(int value){
            return new Continuation<int,int>(value, x => x * 10);
        }

        [Test]
        public void ContinuationMonadTests()
        {

            var number = 42;
            var result = number.Unit(x => x + 8).FMap(x => x * 2).Bind(Wrapped).Run();

            Console.WriteLine(result);
        }
    }
}
Теги:
haskell
functional-programming
monads

2 ответа

2
Лучший ответ

Это не продолжение монады. Вы гораздо ближе к экземпляру Haskell Monad для функций.

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

Сложим что-то вроде экземпляра Haskell Monad для функций в С#. Синтаксис LINQ установил соглашение для Monad в С#. Они должны иметь:

  • a Select метод расширения, аналогичный fmap Haskell Functor
  • метод SelectMany аналогичный Haskell Monad >>=
  • дополнительный SelectMany который использует синтаксис LINQ. Это требует дополнительной функции, которая объединяет значение из двух шагов вместе.

К сожалению, нет никакой конвенции, по которой должен быть вызван аналог return Monad; мы будем называть нашу Constant. К сожалению, Constant не будет очень удобен, потому что вывод типа С# не сможет определить типы.

public static class Function
{
    public static Func<TIn, TOut> Constant<TIn, TOut>(TOut result)
    {
        return x => result;
    }

    public static Func<TIn, TOut> Select<TIn, TMid, TOut>(
        this Func<TIn, TMid> func,
        Func<TMid, TOut> proj)
    {
        return x => proj(func(x));
    }

    public static Func<TIn, TOut> SelectMany<TIn, TMid, TOut>(
        this Func<TIn, TMid> func,
        Func<TMid, Func<TIn, TOut>> proj)
    {
        return x => proj(func(x))(x);
    }

    public static Func<TIn, TOut> SelectMany<TIn, TMid1, TMid2, TOut>(
        this Func<TIn, TMid1> func,
        Func<TMid1, Func<TIn, TMid2>> proj1,
        Func<TMid1, TMid2, TOut> proj2)
    {
        return x => {
            var mid1 = func(x);
            var mid2 = proj1(mid1)(x);
            return proj2(mid1, mid2);
        };
    }
}

Обратите внимание, что определение этих методов расширения позволяет вам взаимодействовать с чем-то вроде Monad, это не позволяет вам писать код, который является общим для конкретной используемой Monad. Там набросок, как это сделать во второй половине этого ответа.

  • 0
    Ваш ответ всплыл в тот же момент, когда я просто нажал кнопку: D - +1 для лучшего ответа
  • 1
    почему Func<,> может быть первым значением метода расширения? Кажется, хорошо работает на первый взгляд: gist.github.com/CarstenKoenig/26e46150c6458609d756
2

Это может быть немного основано на мнениях, но я постараюсь дать вам мой 5ct в любом случае.

Давайте посмотрим на ваш класс и их экземпляры:

Он включает в себя значение и функцию, где вы (пытались) сделать все это ленивым. С теоретической точки зрения я не вижу разницы с Lazy<T> на первый взгляд:

Вы можете, безусловно, преобразовать одно из ваших " Continuation<Input,Output> в просто Lazy<Output>.

То же самое верно для обратного: при некотором ленивом значении a вы можете сделать экземпляр с помощью просто

new Continuation(a, x => x)

Поэтому мне кажется, что вы просто заново изобрели Lazy (который является монадой, в Haskell вы бы назвали ее Identity.

Cont монада на самом деле не так просто, но она больше связана с.net-событиями или.net-Observables. Сама структура данных была бы

Func<Func<Input,Output>, Output>

Где вы передаете продолжение Func<Input,Output> в некоторый внутренний расчет, а затем struture, чем назовёте его, когда он вычислил входной Input чтобы получить окончательный результат.

Это может быть немного загадочно, но одно.net-приложение - это рабочие процессы Async используемые F #, и которая в некотором смысле была моделью для асинхронного/ожидающего поведения С#.

У меня есть материал, который я использовал для разговора о простой версии этой монады в С# на github, может быть, вы найдете ее интересной.

  • 0
    +1 за признание того, что Lazy<> является Identity Haskell. В c # идиоматическая Identity , вероятно, будет строгой.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню