How to compile a decorator - C ++, Python and its own implementation. Part 2

Decorators are one of the most unusual features of Python. It is a tool that can only exist fully in a dynamically typed, interpreted language. In the first part of the article, my friendWitcher136 showed how to implement in C ++ the most approximate to the standard (Python) version of decorators.



I'll tell you about how I decided to try to implement decorators in a compiled programming language , for which I ended up writing my own small Haskell compiler based on LLVM .





Table of contents



  1. How decorators work in Python
  2. Haskell and LLVM - Native Compiler
  3. So how do you compile a decorator?




How decorators work in Python



Before diving into the decorator compilation algorithm, let's talk about the implementation of decorators in python, and why it cannot be reproduced in the same form in a compiled language. I note right away that in this article, python is understood as CPython. All engine compartment parts refer only to it.



, , , , — , .



Python, - , :



decorator, func, old. newold


def decorator(func):
    def new(*args, **kwargs):
        print('Hey!')
        return func(*args, **kwargs)
    return new

@decorator
def old():
    pass

# old()  "Hey!" -   old    new


— , -, — , .



CPython

Python-. , - — , , , . , , , — - "" .



, , , — - "" . : BINARY_SUBSTRACT () TypeError, 1 'a'. , STORE_FAST ( ), , , TypeError, .. STORE_FAST — .



, new — . -, , , decorator old.



1. —



. decorator , :



name = input('  ')

def first(func):
    ...  #  

def second (func):
    ...  #  

if name == 'first':
    decorator = first
elif name == 'second':
    decorator = second
else:
    decorator = lambda f: f   #    

@decorator 
def old():
    pass


, old . (, C++) , (- ), . Python — , , , " ", .



, , old void-, , — , , , .



, Python, : .



2. Python



def decorator(func):
    def two_args(x, y):
        ...
    return two_args

@decorator
def one_arg(x):
    ...


, . one_arg , ( ) — , , , (, "" ). , ? " " . , , decorator -, .



, , , — . , .



— — func? , , — , . func A, A. void* func, , .



func , — Witcher136 . , (. C++ ).






. :



  • — ?
  • — ?
  • , , ( )


, Python — . , — Python — .

— " ", , , . , .



.





Haskell LLVM —



Haskell, , LLVM . Haskell llvm-hs, LLVM. Parsec, , - ( , , Parsec — parser combinators).



, Grit, ( , , ) — . .



Grit — expression-oriented ()



Grit, , if-else, , — , .



int main() = {
    int i = 0;
    i = i + if(someFunction() > 0) {
        1;
    }
    else {
        0;
    };
};


, i 1, someFunction , , 0 .



return



, ( ) .



, — , Grit, — , . returns, — , ; .



, , "" — "", — , .



int simple(int x) = {
    /* 
          
        x   y
    */
    int y = someOtherFunction();
    x + y;
};

/*
   ,    ,    .
      ,   
*/
int incr(int x) = x + 1;

int main() returns statusCode {
    /*
             returns
         ,  
           .
         "" 
         ,     
    */
    int statusCode = 0;
    int result = someFunction();
    if (someFunction < 0) {
        statusCode = 1;
    };
};


Auto — Grit



Grit auto, , ( ) .



— , . — — , — ..

, , returns.



auto half (int x) = x / 2;   //   incr    float





(expression-oriented), return () — Grit. , .

, , .



— ?





?



, , — runtime compile-time, .



, , , — , .



-, Grit — , ( AST, abstract syntax tree), . -, , .



, :



@auto flatten = {
    auto result = @target;
    if (result < 0) {
        0;
    }
    else {
         result;
    };
};


, , 0, 0, .



@auto flattenflatten @auto — , (@ — , , ).



. , — , , , .



@target. , . ( ), , , , ( ).

, AST @target, . , , — . , .



, Grit, — , Python.



, :



@auto lockFunction = {
    mutex.lock();
    @target
};


, - :



@auto optional = if (checkCondition()) {
    @target;
}
else {
    someDefaultValue;
};




Grit :



@auto flatten = {
    auto result = @target;
    if (result < 0) {
        0;
    }
    else {
         result;
    };
};

@flatten
int incr(int x) = x+1;


flatten , .



"" , - :



Decorator "flatten" auto {
  BinaryOp = (Def auto "result") (DecoratorTarget)
  If (BinaryOp < (Var "result") (Int 0)) {
    Int 0
  }
  else {
    Var "result"
  }
}
Function "incr" int ; args [Def int "x"] ; modifiers [Decorator "flatten"] ; returns Nothing {
  BinaryOp + (Var "x") (Int 1)
}


, — Decorator, incr , Decorator "flatten". DecoratorTargetincr.



, — . , , , "" — , .



, :



Function (int -> int) incr ["x"] {
  BinaryOp i= (Def int "result") (
    Block int {
      BinaryOp i+ (Var int "x") (int 1)
    }
  )
  If int (BinaryOp b< (Var int "result") (int 0)) {
    int 0
  }
  else {
    Var int "result"
  }
}


:



  • — AST, .
  • incr — , flatten, DecoratorTarget Block {...} — " ", . , , — int "result". BinaryOp i= int-, result auto — , , .


, , , . Python, , , Grit.



, — , , :



@auto lockF(mutex M) {
    M.lock();
    @target;
};

@lockF()
int someFunction(...)


mutex M, "", (, , Python — ).



, @args, , " " . , @args.length — , @args.1 — . - , - — .



, Haskell , , , , . , ( , ), - .



, stack



PS It was a very interesting and unusual experience for me - I hope that you too were able to learn something useful from this story. If you need a separate article about writing a LLVM-based Haskell compiler - write in the comments.

I will try to answer any questions in the comments, or in a telegram - @ nu11_pointer_exception




All Articles