Метапрограммирование со статической типизацией?

Я думал о том, чего мне будет не хватать при переносе некоторого кода Python на статически типизированный язык, такой как F# или Scala; библиотеки можно заменить, краткость сравнима, но у меня много кода на Python, который выглядит следующим образом:

@specialclass
class Thing(object):
    @specialFunc
    def method1(arg1, arg2):
        ...
    @specialFunc
    def method2(arg3, arg4, arg5):
        ...

Где декораторы делают огромный объем работы: заменяют методы вызываемыми объектами с состоянием, дополняют класс дополнительными данными и свойствами, и так далее. Хотя Python позволяет динамическое метапрограммирование "обезьяньей заплатки" в любом месте, в любое время, кем угодно, я обнаружил, что по существу все мое метапрограммирование выполняется в отдельной "фазе" программы. Т.е.:

load/compile .py files
transform using decorators
// maybe transform a few more times using decorators
execute code // no more transformations!

Эти фазы в основном совершенно разные; я не запускаю никакого кода уровня приложения в декораторах, и не выполняю никаких ниндзя-замен класса на другой класс или замен функции на другую функцию в основном коде приложения. Хотя "динамичность" языка говорит о том, что я могу делать это где угодно, я никогда не занимаюсь заменой функций или переопределением классов в основном коде приложения, потому что это очень быстро становится безумием.

По сути, я выполняю единственную перекомпиляцию кода перед тем, как начать его выполнять.

Единственное похожее метапограммирование, о котором я знаю в статически типизированных языках, это отражение: т.е. получение функций/классов из строк, вызов методов с использованием массивов аргументов и т.д. Однако это, по сути, превращает статически типизированный язык в динамически типизированный, теряя всю безопасность типов (поправьте меня, если я ошибаюсь?). В идеале, я думаю, должно получиться что-то вроде следующего:

load/parse application files 
load/compile transformer
transform application files using transformer
compile
execute code

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

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

Итак, мой вопрос в том, возможно ли это? Было ли это уже сделано в каком-то языке или фреймворке, о котором я не знаю? Теоретически это невозможно? Я не очень хорошо знаком с теорией компиляторов или формальных языков, я знаю, что это сделает шаг компиляции тьюрингово полным и без гарантии завершения, но мне кажется, что это то, что мне нужно, чтобы соответствовать удобной трансформации кода, которую я получаю в динамическом языке, сохраняя при этом статическую проверку типов.

EDIT: Одним из примеров использования может быть полностью универсальный декоратор кэширования. В python это было бы так:

cacheDict = {}
def cache(func):
    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        cachekey = hash((args, kwargs))
        if cachekey not in cacheDict.keys():
            cacheDict[cachekey] = func(*args, **kwargs)
        return cacheDict[cachekey]
    return wrapped


@cache
def expensivepurefunction(arg1, arg2):
    # do stuff
    return result

Хотя функции более высокого порядка могут делать кое-что из этого или объекты с функциями-внутри могут делать кое-что из этого, AFAIK они не могут быть обобщены для работы с любой функцией, принимающей произвольный набор параметров и возвращающей произвольный тип, сохраняя при этом безопасность типов. Я мог бы сделать что-то вроде:

public Thingy wrap(Object O){ //this probably won't compile, but you get the idea
    return (params Object[] args) => {
        //check cache
        return InvokeWithReflection(O, args)
    }
}

Но все приведения полностью убивают безопасность типов.

EDIT: Это простой пример, где сигнатура функции не меняется. В идеале то, что я ищу, может модифицировать сигнатуру функции, изменяя входные параметры или тип выхода (например, композиция функций), сохраняя при этом проверку типов.

21
задан Li Haoyi 27 November 2011 в 03:13
поделиться