Явные контексты в языках программирования
От: 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);
}


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

Как вам такая идея расширения языка? Может в каких-нибудь языках это уже реализовано?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.