Как сделать активные паттрены.
От: WolfHound  
Дата: 19.10.10 09:18
Оценка: 26 (3)
Ответ на: Re[12]: Scala / F# / Nemerle и мейнстрим
Автор: VladD2
Дата: 19.10.10

Получилось много и по делу. По этому пишу сюда.

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

WH>>Всетки активные паттерны это очень мощьная штука.

VD>Это очень медленная штука. Как я понимаю, они там создают копии объектов.
То что авторы F# тормоза не значит что и я тормоз
Там можно все сделать очень эффективно.

VD>У меня на этот вопрос есть свое мнение. Можно просто описывать так называемый виртуальный (не существующий в реалии) конструктор который будет сообщать компилятору какие поля или свойства использовать для эмуляции конструктора. Тогда можно будет любой объект любого класса "можно будет матчить прямо как варианты".

Тут можно сделать все еще интересней.
1)Можно в паттернах использовать функции.
Первый аргумент (в случае методов this) будет принимать тот объект который матчим.
Если есть еще аргументы их нужно будет указать явно.
Возвращаемое значение и out параметры будут возвращаемыми значениями паттерна.
Их можно связать с переменной или счемнибудь сматчить.
Тогда можно будет делать так
match (someString)
{
    | string.IsNullOrEmpty(true) =>
    | int.TryParse(value, true) =>
    | Contains("qwe", true) =>
    | Contains("asd", true) =>
    | Contains("zxc", true) =>
}

Для функций возвращающих bool имеет смысл ввести сахар который матчит возвращаемое значенис с true.
Тогда код станет таким
match (someString)
{
    | string.IsNullOrEmpty =>
    | int.TryParse(value) =>
    | Contains("qwe") =>
    | Contains("asd") =>
    | Contains("zxc") =>
}

match (someDictionary)
{
    | TryGetValue("qwe", value) =>
    | TryGetValue("asd", value) =>
    | TryGetValue("zxc", value) =>
}

Эта функциональность перекрывает твой "виртуальный конструктор" с большим запасом.
Просто пишем такую функцию:
SomeTypeVirtualConstructor(obj : SomeType, field1 : out Type1, field3 : out Type4, field6 : out Type21) : void
{
    field1 = obj.field1;
    field3 = obj.field3;
    field6 = obj.field6;
}

В качестве бонуса можно возвращать bool и проверять состояние объекта.
Можно будет для этого даже макру сделать.

2)Функции конечно хорошо но bool не всегда достаточно.
Иногда при анализе объекта может выясниться что есть более чем один вариант который матчится или нет.
Например:
Можно написать так
variant EvenOdd
{
    | Even
    | Odd
}
IsEvenOdd(value : int) : EvenOdd
{
    if (value % 2 == 0) Even() else Odd()
}
match (someInt)
{
    | IsEvenOdd(Even) =>
    | IsEvenOdd(Odd) =>
}

Но это громоздко и не эффективно.

Для этого можно ввести такую конструкцию:
pattern EvenOdd
{
    | Even
    | Odd

    match (value : int)
    {
        if (value % 2 == 0) Even() else Odd()
    }
}
match (someInt)
{
    | Even =>
    | Odd =>
}

Рассахаривается это во что-то типа:
class EvenOdd
{
    public enum Patterns
    {
        | Even
        | Odd
    }

    public Match (value : int) : Patterns
    {
        if (value % 2 == 0) Even() else Odd()
    }
}


Если у нас варианты с параметрами то получится что-то типа такого
pattern Asdasdasd
{
    | Asd { a : int; b : string; x : float; }
    | Qwe { c : string; d : int; }

    match (value : SomeType)
    {
        ...
    }
}

Рассахаривается это во что-то типа:
class Asdasdasd
{
    public enum Patterns
    {
        [ArgumentsMap("1 2 3", "a b x")]//Это метаданные для того чтобы из других сборок можно было использовать
        | Asd
        [ArgumentsMap("2 1", "c d")]
        | Qwe
    }

    public Match (value : SomeType, res1 : out int, res2 : out string, res3 : out float) : Patterns
    {
        ...
    }
}

Таким образом никаких лишних выделений памяти не происходит.
На практике поля внутри разных опций одного варианта не отличаются разнообразием типов. По этому out параметры функции будут очень хорошо склееваться.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.