Branch monad, Pattern matching

6yrop

По аналогии с Option type хочется вот такое:
 

void M1(DateTime d)
{
ToHtml(d.Branch1(Type<string>.Get;
}

void M2(string s)
{
ToHtml(s.Branch2(Type<DateTime>.Get;
}

void ToHtml(IBranch<DateTime, string> value)
{
Console.WriteLine("<div>" + value.Match(_ => _.ToString("yyyy-MM-dd" _ => _) + "</div>");
}

Как такое называет? И как с этим в разных языках?
Полный код:
 

public interface IBranch<out T1, out T2>
{
T Match<T>(Func<T1, T> branch1, Func<T2, T> branch2);
}

public static class Branch
{
public static IBranch<T1, T2> Branch1<T1, T2>(this T1 branch1, Type<T2> branch2)
{
return new Branch1Impl<T1, T2>(branch1);
}

public static IBranch<T1, T2> Branch2<T1, T2>(this T2 branch2, Type<T1> branch1)
{
return new Branch2Impl<T1, T2>(branch2);
}

private class Branch2Impl<T1, T2> : IBranch<T1, T2>
{
private readonly T2 branch2Value;

public Branch2Impl(T2 branch2Value)
{
this.branch2Value = branch2Value;
}

public T Match<T>(Func<T1, T> branch1, Func<T2, T> branch2)
{
return branch2(branch2Value);
}
}

private class Branch1Impl<T1, T2> : IBranch<T1, T2>
{
private readonly T1 branch1Value;

public Branch1Impl(T1 branch1Value)
{
this.branch1Value = branch1Value;
}

public T Match<T>(Func<T1, T> branch1, Func<T2, T> branch2)
{
return branch1(branch1Value);
}
}
}

public class Type<T>
{
public static readonly Type<T> Get = new Type<T>

private Type
{
}
}

karkar

Это ж sum type, известный в теории категорий как копроизведение.
http://ncatlab.org/nlab/show/coproduct
Монадой не является. В нормальных языках доступен из коробки в виде алгебраических типов. В хаскеле алгебраики - чуть более общая штука, сочетающая сумму и произведение. В окамле это именно сумма, скорее. Твой частный случай для двух типов в хаскеле известен как Either.

6yrop

спасибо :)

bleyman

копроизведение
Какое глубокое, переполненное смыслами слово!

karkar

Другое название - гуаноэкстерминация. :)

6yrop

В нормальных языках доступен из коробки в виде алгебраических типов.
т.е. даже в продвинутых языках на каждый такой случай требуется городить специальный именованный тип? Идеальным был бы синтаксический сахар в стиле Typescript:
 

void M1(d: DateTime)
{
ToHtml(m1 => m1(d;
}

void M2(s: string)
{
ToHtml(m2 => m2(d;
}

void ToHtml(arg<T>: (m1: DateTime => T, m2: string => T) => T)
{
return Console.WriteLine("<div>" + arg(_ => _.ToString("yyyy-MM-dd" _ => _) + "</div>")
}

karkar

Если там пара вариантов, как в твоем примере, то делаешь аргумент например Either Date String и его передаешь. Если вариантов больше, то можно завести новый тип (конкретный или тоже генерик а можно собрать из Either - Either Date (Either String Int).

6yrop

делаешь аргумент например Either Date String и его передаешь.
не удобно, поскольку надо помнить что такое Left что такое Right (типы могу совпасть).
Если вариантов больше, то можно завести новый тип (конкретный или тоже генерик)

анонимные типы тут явно легковеснее смотрятся
а можно собрать из Either - Either Date (Either String Int).

а если в конкретной ветке надо будет передать несколько параметров, таплы будете использовать? а хочется не по номеру позиции, а именованные параметры иметь. Подход Typescript-а прямолинейнее выглядит.

6yrop

задача то крайне проста: передать параметры (между call и callback-ами)! Решение ее через алгебраические типы, имхо, не самое оптимальное (хотя лучше чем C#/Java).

apl13

О, как раз как я люблю!
car :: Either a b -> a
car (Left a) = a

cdr :: Either a b -> b
cdr (Right b) = b

karkar

Откуда ты знаешь, удобно это или нет, если никогда не пробовал?
Описать нужный тип - одна строчка. На практике, если в функцию передается "или это или это или это, и их все объединяет способность превращаться в то-то", то часто это оказываются типы одного тайпкласса например. В таком случае явная сумма и не нужна. В других же случаях этот набор типов часто по логике программы уже объединен в какой-то осмысленный тип, вроде узла AST. Совсем уж произвольным вариантам как-то неоткуда взяться.

6yrop

Откуда ты знаешь, удобно это или нет, если никогда не пробовал?
что не пробовал то, объявить variants в Nemerle или discriminated unions в F#? По-моему, очевидно, надо придумывать название типа => неудобно (если нет дополнительных факторов).
 
... неоткуда взяться.

ну пока нет выразительного средства кажется, что неоткуда взяться. :smirk: Средство формирует способ мышления, это да. :)

6yrop

надо придумывать название типа => неудобно
понятие определяется двумя словами (m1, m2); зачем для него придумывать идентификатор из одного слова?

6yrop

т.е. тут также как с обычными функциями, если функция принимает 1-5 параметров, тип контейнер можно не вводить. Если параметров много, вводим специальный тип контейнер. Хочет иметь такую же возможность для двойного колбека.

karkar

Ок, давай 5 реалистичных примеров, где это пригодилось бы.
Данный выше не годится - его покрывает тайпкласс Show.

karkar

что не пробовал то, объявить variants в Nemerle или discriminated unions в F#?
Ну да. Я почему-то был уверен, что однажды ночью к тебе пришел Баллмер и под страхом анальной кары запретил использовать что-либо кроме C#.

6yrop

там нет ReSharper-а :D
Действительно, писать код в форме, которая делает возможной машинную обработку кода, и при этом не иметь софта для такой обработки. Это странно. (Понятно, что есть какие-то инструменты, но до продуктов jetbrains-а они вряд ли дотягивают).
Оставить комментарий
Имя или ник:
Комментарий: