Явные контексты в языках программирования
От: Aleх  
Дата: 05.01.13 23:35
Оценка:
В статически типизированных императивных (мультипарадигменных) языках программирования обычно есть несколько областей видимости сущностей языка (назовем их контекстами):
— глобальный контекст, содержащий глобальные переменные и функции
— локальный — локальные переменные
— контекст класса — члены данные и методы класса, который активен в момент выполнения метода
— контекст замыкания лямбда функции

Возникла идея обобщить это неявное понятие контекста и вынести возможность управления контекстами непосредственно в язык.
Расcмотрим случаи, когда это может понадобиться. Примеры особенно актуальны для языка С++

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

Обычно, функциональность, отвечающая за аллокацию и освобождение памяти для объектов, жестко зашита в коде и поменять схему работы выделения/освобождения памяти очень сложно в большом проекте.
Аллокаторы заданы глобально. Это либо могут быть перегруженные операторы new/delete у каждого класса, либо глобальные функции allocate/deallocate.
Такая задача, как использование разных аллокаторов для различных групп объектов, определяемых в runtime, практически не реализуема — прийдется в каждую функцию в коде добавить ещё один параметр — объект аллокатора.

2. Запись лога и ввод/вывод. Как и в предыдущем случае не понятно что делать с функциями и объектами, отвечающими за ввод/вывод и ведение лога.
Обычно программисту предоставляется две альтернативы — либо делать их глобальными объектами, но тогда потеряется гибкость, либо добавлять в каждую функцию объекты Logger и IO, что потребует написание boilerplate кода.

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

Вот как это могло бы выглядеть в C++

struct Logger
{
    void writeMessage(std::string const &);
    
    Logger(...) {...}
};

template<class IStream, class OStream> struct IO
{
    IStream & in;
    OStream & out;
    
    IO(...) {...}
};

template<class Allocator> struct Memory
{
    void * operator new (std::size_t size) throw (std::bad_alloc);
    void operator delete (void s* ptr) throw ();
};


template<class IStream, class OStream, Allocator> [IO<IStream, OStream>, Memory<Allocator>]
{
    [Logger] void bar(char b)
    {
        int * pInt = new int;
        out << "Int object created. \n";
        writeMessage("Int object created. \n");
    }    
    
    struct Graph
    {
        struct Node
        {
        
        };
        
        readGraph()
        {
            ...
            ... = new Node(...); //Pool allocator new
            ...
        }
        ~Graph()
        {
            ...
            delete ptr;
            ...
        }
    };    
    
    void foo(int a)
    {
        [Logger("logFile")] bar(a);
        //OR
        [Logger("logFile")] 
        {
            writeMessage("hello. \n");
            bar(a);
        }
        
        [Memory<PoolAllocator>()]
        {
            Graph g;
            g.readGraph();
        }
        
        //OR
        [Memory<PoolAllocator>()] Graph g; 
        //Создается объект, у которого перед вызовом 
        //каждой функции и деструктора будет происходить смена контекста 
        //Memory на Memory<PoolAllocator>()
        
        g.readGraph();
        
        auto readGraphMethod = g.readGraph; //замыкание, содержащее контекст и указатель на метод
        
    }
}

void main()
{
    [IO<std::istream, std::ostream>(cin, cout), Memory<SimpleAllocator>()] foo(123);
    [IO<std::ifstream, std::ofstream>(std::ifstream("inFile.txt"), std::ofstream("outFile.txt")),
        Memory<FastAllocator>()] foo(456);
}


От возможности определения глобальных переменных и функций при наличии контекстов можно будет избавиться.

Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?
Re: Явные контексты в языках программирования
От: AlexCab LinkedIn
Дата: 06.01.13 07:05
Оценка:
Здравствуйте, Aleх, Вы писали:
A> [Logger("logFile")]
A> {
A> writeMessage("hello. \n");
A> bar(a);
A> }
В чём отличие от:
Logger.writeMessage("hello. \n");
Logger.bar(a);

?
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re: Явные контексты в языках программирования
От: LaptevVV Россия  
Дата: 06.01.13 08:42
Оценка:
Здравствуйте, Aleх, Вы писали:

A>В статически типизированных императивных (мультипарадигменных) языках программирования обычно есть несколько областей видимости сущностей языка (назовем их контекстами):

A>- глобальный контекст, содержащий глобальные переменные и функции
A>- локальный — локальные переменные
A>- контекст класса — члены данные и методы класса, который активен в момент выполнения метода
A>- контекст замыкания лямбда функции

Вы забыли еще контекст модуля.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Явные контексты в языках программирования
От: Abyx Россия  
Дата: 06.01.13 11:04
Оценка:
Здравствуйте, Aleх, Вы писали:

A>Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?


выглядит как монады.
In Zen We Trust
Re: Явные контексты в языках программирования
От: os24ever
Дата: 06.01.13 12:28
Оценка:
A>Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?

Замыкания в JavaScript?
И вот недавнее обсуждение
Автор: Lloyd
Дата: 18.03.12
.
Re[2]: Явные контексты в языках программирования
От: Aleх  
Дата: 06.01.13 12:35
Оценка:
Здравствуйте, os24ever, Вы писали:

A>>Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?


O>Замыкания в JavaScript?

Да, я знаю, что в JavaScript можно похожие вещи делать. Поэтому я в начале писал про статически типизированные языки.

O>И вот недавнее обсуждение
Автор: Lloyd
Дата: 18.03.12
.


L>Передать в конструктор — не вариант?

Не вариант, тк:
— придется передавать через множество функций (которые поптом передадут в конструктор), а это значит, что в каждой функции нужен ещё дополнительный параметр
— если таких объектов много (десятки — сотни тысяч), то указатель на логгер (а обычно нужен не только логгер, но и много чего другого) будет храниться в каждом объекте, что вызовет сильный оверхед по памяти.
Re[2]: Явные контексты в языках программирования
От: Aleх  
Дата: 06.01.13 12:50
Оценка:
Здравствуйте, AlexCab, Вы писали:

AC>Здравствуйте, Aleх, Вы писали:

A>> [Logger("logFile")]
A>> {
A>> writeMessage("hello. \n");
A>> bar(a);
A>> }
AC>В чём отличие от:
AC>
AC>Logger.writeMessage("hello. \n");
AC>Logger.bar(a);
AC>

AC>?
А если bar — метод класса Graph, как будет выглядеть вызов?

Cуть вот в чем:

template<class IStream, class OStream, Allocator> [IO<IStream, OStream>, Memory<Allocator>, Logger]
{
    void f1(char b)
    {
        writeMessage("Message. \n");
        f2(b);
        f3(b);
    }
    void f2(char b)
    {
        writeMessage("Message. \n");
        f4(b);
        f5(b);
    }
    void f3(char b)
    {
        writeMessage("Message. \n");
        f6(b);
        f7(b);
    }
    void f4(char b)
    {
        int * pInt = new int;
        out << "Int object created. \n";
        writeMessage("Message. \n");
    }
    void f5(char b)
    {
        int * pDouble = new double;
        out << "Double object created. \n";
        writeMessage("Message. \n");
    }
    void f6(char b)
    {
        int * pFloat = new float;
        out << "Float object created. \n";
        writeMessage("Message. \n");
    }
    void f7(char b)
    {
        int * pChar = new char;
        out << "Char object created. \n";

        writeMessage("Message. \n");
    }
}

VS
template<class IStream, class OStream, Allocator> void f1(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    logger.writeMessage("Message. \n");
    f2(io, memory, logger, b);
    f3(io, memory, logger, b);
}
template<class IStream, class OStream, Allocator> void f2(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    logger.writeMessage("Message. \n");
    f4(io, memory, logger, b);
    f5(io, memory, logger, b);
}
template<class IStream, class OStream, Allocator> void f3(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    logger.writeMessage("Message. \n");
    f6(io, memory, logger, b);
    f7(io, memory, logger, b);
}
template<class IStream, class OStream, Allocator> void f4(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    int * pInt = memory.allocate<int>();
    io.out << "Int object created. \n";
    logger.writeMessage("Message. \n");
}
template<class IStream, class OStream, Allocator> void f5(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    int * pDouble = memory.allocate<double>();
    io.out << "Double object created. \n";
    logger.writeMessage("Message. \n");
}
template<class IStream, class OStream, Allocator> void f6(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    int * pFloat = memory.allocate<float>();
    io.out << "Float object created. \n";
    logger.writeMessage("Message. \n");
}
template<class IStream, class OStream, Allocator> void f7(IO<IStream, OStream> & io, Memory<Allocator> & memory, Logger & logger, char b)
{
    int * pChar = memory.allocate<char>();
    io.out << "Char object created. \n";

    logger.writeMessage("Message. \n");
}
Re[3]: Явные контексты в языках программирования
От: AlexCab LinkedIn
Дата: 06.01.13 14:00
Оценка:
A>Cуть вот в чем:
A>
A>template<class IStream, class OStream, Allocator> [IO<IStream, OStream>, Memory<Allocator>, Logger]
A>{
A>    void f1(char b)
A>    {
A>        writeMessage("Message. \n");
A>        f2(b);
A>        f3(b);
A>    }
A>    void f2(char b)
A>    {
A>        writeMessage("Message. \n");
A>        f4(b);
A>        f5(b);
A>    }
A>    void f3(char b)
A>    {
A>        writeMessage("Message. \n");
A>        f6(b);
A>        f7(b);
A>    }
A>    void f4(char b)
A>    {
A>        int * pInt = new int;
A>        out << "Int object created. \n";
A>        writeMessage("Message. \n");
A>    }
A>    void f5(char b)
A>    {
A>        int * pDouble = new double;
A>        out << "Double object created. \n";
A>        writeMessage("Message. \n");
A>    }
A>    void f6(char b)
A>    {
A>        int * pFloat = new float;
A>        out << "Float object created. \n";
A>        writeMessage("Message. \n");
A>    }
A>    void f7(char b)
A>    {
A>        int * pChar = new char;
A>        out << "Char object created. \n";

A>        writeMessage("Message. \n");
A>    }
A>}
A>

Это как-бы объединение нескольких областей видимости имён в одну, типа множественного наследования но только не с классами а с объектами.
Например вместо:
class C1 {
  def foo() = {}
  new C2(this)
}
class C2 (val c1:C1) {
  c1.foo() 
}

Можно писать так:
class C1 {
  def foo() = {}
  new C2(this)
}
class C2 [C1] {
  foo() 
}

Я правильно понял?
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[4]: Явные контексты в языках программирования
От: Aleх  
Дата: 06.01.13 14:27
Оценка: 2 (1)
Здравствуйте, AlexCab, Вы писали:

AC>Это как-бы объединение нескольких областей видимости имён в одну, типа множественного наследования но только не с классами а с объектами.

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

AC>Например вместо:

AC>
AC>class C1 {
AC>  def foo() = {}
AC>  new C2(this)
AC>}
AC>class C2 (val c1:C1) {
AC>  c1.foo() 
AC>}
AC>

AC>Можно писать так:
AC>
AC>class C1 {
AC>  def foo() = {}
AC>  new C2(this)
AC>}
AC>class C2 [C1] {
AC>  foo() 
AC>}
AC>

AC>Я правильно понял?

Примерно так, но предлагается иметь возможность задавать контекст не только классам, но и функциям.
Re[5]: Явные контексты в языках программирования
От: AlexCab LinkedIn
Дата: 06.01.13 15:05
Оценка:
AC>>Это как-бы объединение нескольких областей видимости имён в одну, типа множественного наследования но только не с классами а с объектами.
A>Да, в общем это так. При множественном наследовании классы, а вместе с ними и создаваемые родительские объекты, зафиксированы в определении класса, а тут предлагается задавать объекты, чьи области видимости сольются в одну в момент создания объекта. То есть если рассматривать это в терминах множественного наследования, то создаваемому объекту можно передать в качестве родительских уже давно созданные объекты.
Прикольно, хоть и не очень хорошо с точки зрения "интуитивной понятности"(выглядит как обращение к собственным членам класса).
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re: Явные контексты в языках программирования
От: avp_  
Дата: 07.01.13 17:59
Оценка:
В С++ возникнет проблема с линковкой такого... Тут только динамические
языки могут что то предложить.

Но чисто теоретически, первый шаг на этом направлении уже сделан.
Фактически класс — это вручную созданный контекст, т.к. неявно в каждый
метод передаётся this-указатель на контекст.
Проблема только в том, что контексты классов нельзя каскадировать, т.е.
при создании класса всегда берётся в качестве предка глобальный контекст.
Если бы при создании объекта можно было бы указать объект-предок,
контекст которого был бы виден во всех методах вновь созданного класса..
Posted via RSDN NNTP Server 2.1 beta
Re: Явные контексты в языках программирования
От: . Великобритания  
Дата: 07.01.13 21:59
Оценка:
Здравствуйте, Aleх, Вы писали:

A> Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?

Вроде типичный IoC, но только с поддержкой на уровне языка, т.к. С++ не поддерживает рефлексии. На всяких java давно подобное сделано в виде IoC-containers, благодаря рефлексии и аннотациям. Да в общем-то можно и на C++ писать в таком стиле, если правильно дизайнить, используя всякие шаблоны типа фабрик, делегатов, етс. Хоть и получается много boilerplate-кода, зато compile-time проверок больше будет.
Ещё плохо, что в твоём примере, скажем, используется неявный пул, хз откуда берущийся в строчке "new Node(...); //Pool allocator new", особенно интересно в случая рекурсий и многопоточного программирования, это же типичная глобальная переменная "текущего" пула.
avalon/1.0.432
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Явные контексты в языках программирования
От: Aleх  
Дата: 07.01.13 22:38
Оценка:
Здравствуйте, avp_, Вы писали:

_>В С++ возникнет проблема с линковкой такого... Тут только динамические

_>языки могут что то предложить.

С линковкой проблема может возникнуть только в случае шаблонных контекстов. И то, там сложности такие же как в реализации export template.

Можно всё это двумя способами реализовывать:
1. Передавать в каждую функцию указатель на контекст.
2. Завести отдельный глобальный стек в который добавлять указатель на контекст (или сам объект контекста) при его изменении и соответственно удалять при выходе из контекста. Такая реализация будет немного экономнее, тк в стеки будет запись не на каждый вызов функции, а только при смене контекста.

Контекст будет представлять собой кортеж/структуру из указателей на составные части (например Memory, Logger, IO).

_>Но чисто теоретически, первый шаг на этом направлении уже сделан.

_>Фактически класс — это вручную созданный контекст, т.к. неявно в каждый
_>метод передаётся this-указатель на контекст.
_>Проблема только в том, что контексты классов нельзя каскадировать, т.е.
_>при создании класса всегда берётся в качестве предка глобальный контекст.
_>Если бы при создании объекта можно было бы указать объект-предок,
_>контекст которого был бы виден во всех методах вновь созданного класса..

В Java есть внутренние классы.
Re[2]: Явные контексты в языках программирования
От: Aleх  
Дата: 07.01.13 22:59
Оценка:
Здравствуйте, ., Вы писали:

.>Здравствуйте, Aleх, Вы писали:


A>> Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?

.>Вроде типичный IoC, но только с поддержкой на уровне языка, т.к. С++ не поддерживает рефлексии. На всяких java давно подобное сделано в виде IoC-containers, благодаря рефлексии и аннотациям. Да в общем-то можно и на C++ писать в таком стиле, если правильно дизайнить, используя всякие шаблоны типа фабрик, делегатов, етс. Хоть и получается много boilerplate-кода, зато compile-time проверок больше будет.
Ну вот, поддержка на уровне языка нужна, чтобы не было boilerplate-кода, а так, да, можно всё это и сейчас делать, только неудобно.

.>Ещё плохо, что в твоём примере, скажем, используется неявный пул, хз откуда берущийся в строчке "new Node(...); //Pool allocator new", особенно интересно в случая рекурсий и многопоточного программирования, это же типичная глобальная переменная "текущего" пула.


Пул берется отсюда:


void foo(int a)
    {
        ...
        [Memory<PoolAllocator>()] Graph g; 
        ...
    }


При выходе из функции foo пул разрушается.
Рекурсия пулу никак не помешает. Единственное — в зависимости от потребностей, его нужно будет создать либо один на всё дерево рекурсии, либо при спуске на каждом уровне вниз создавать и разрушать ври выходе из функции.

При многопоточном программировании, в каждом потоке будет свой контекст. Вот
Автор: Aleх
Дата: 08.01.13
как предлагается хранить и передавать контексты. Соответственно во втором случае у каждого потока будет свой стек контекстов.
Re[3]: Явные контексты в языках программирования
От: . Великобритания  
Дата: 08.01.13 09:45
Оценка:
Здравствуйте, Aleх, Вы писали:

.>>Вроде типичный IoC, но только с поддержкой на уровне языка, т.к. С++ не поддерживает рефлексии. На всяких java давно подобное сделано в виде IoC-containers, благодаря рефлексии и аннотациям. Да в общем-то можно и на C++ писать в таком стиле, если правильно дизайнить, используя всякие шаблоны типа фабрик, делегатов, етс. Хоть и получается много boilerplate-кода, зато compile-time проверок больше будет.

A>Ну вот, поддержка на уровне языка нужна, чтобы не было boilerplate-кода, а так, да, можно всё это и сейчас делать, только неудобно.
В java от языка достаточно рефлексии, остальное делается на уровне библиотек. Вроде вот есть попытки и для С++ сделать, хотя я не понял, делается ли property injection, без него скучно...

.>>Ещё плохо, что в твоём примере, скажем, используется неявный пул, хз откуда берущийся в строчке "new Node(...); //Pool allocator new", особенно интересно в случая рекурсий и многопоточного программирования, это же типичная глобальная переменная "текущего" пула.

A>Пул берется отсюда:

A>При выходе из функции foo пул разрушается.

Да я понял, просто неявная глобальная (вероятно thread local) переменная. А если в контексте 2 пула понадобится?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Явные контексты в языках программирования
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 08.01.13 10:37
Оценка:
Кажися, в Scala как раз это делают implicit objects.
Re[3]: Явные контексты в языках программирования
От: avp_  
Дата: 08.01.13 17:00
Оценка:
Aleх wrote:

> С линковкой проблема может возникнуть только в случае шаблонных

> контекстов. И то, там сложности такие же как в реализации export
> template.


Если какой то класс пишет внутри метода
Logger.Write("...");
То тип переменной Logger будет неясен в нестабильном контексте.
Соответственно obj файл с этим классом нельзя будет использовать в
сценариях динамического контеста.
Да и просто определение переменнной
foo bar;
требует знания декларации типа foo.
Posted via RSDN NNTP Server 2.1 beta
Re: Явные контексты в языках программирования
От: Tanker  
Дата: 10.01.13 10:22
Оценка:
Здравствуйте, Aleх, Вы писали:

A>Возникла идея обобщить это неявное понятие контекста и вынести возможность управления контекстами непосредственно в язык.


Не нужно обобщать контекст, нужно работать с зависимостями и связыванием. Для контекстов слишком много сценариев что бы всовывать их все в язык, а вот с зависимостями и связыванием гораздо проще.
The animals went in two by two, hurrah, hurrah...
Re[6]: Явные контексты в языках программирования
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.01.13 05:49
Оценка:
Здравствуйте, AlexCab, Вы писали:

AC>>>Это как-бы объединение нескольких областей видимости имён в одну, типа множественного наследования но только не с классами а с объектами.

A>>Да, в общем это так. При множественном наследовании классы, а вместе с ними и создаваемые родительские объекты, зафиксированы в определении класса, а тут предлагается задавать объекты, чьи области видимости сольются в одну в момент создания объекта. То есть если рассматривать это в терминах множественного наследования, то создаваемому объекту можно передать в качестве родительских уже давно созданные объекты.
AC>Прикольно, хоть и не очень хорошо с точки зрения "интуитивной понятности"(выглядит как обращение к собственным членам класса).
Похоже на попытку улучшить синтаксис Dependency Injection.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.