2

I understand that in Solidity if a function has a modifier, this modifier runs first, prior the the logic within the function running. This is done for checks of ownership, checks of state, etc.

In Python, the concept of decorators seems very similar—a decorator replaces the decorated function with a new function that accepts the same arguments and returns whatever the decorated function was supposed to return, while also doing some extra processing.

In terms of what goes on behind the scenes, I know that in Python, the following are equivalent:

@clock
def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)

and

def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)
factorial = clock(factorial)

From my understanding, the following are equivalent in Solidity:

function startSale() notCanceled {
...
}

and

function startSale() {
    notCanceled()
    ...
}

My question is, are these two features meant to perform roughly the same task? When learning solidity, is comparing a decorator to a modifier a fair comparison to make? If so, why? If not, why not?

3

My question is, are these two features meant to perform roughly the same task? When learning solidity, is comparing a decorator to a modifier a fair comparison to make?

Yes, "wrapping" the function is a good way to think about it. It's actually closer to a decorator than the Solidity example in the question.

An example lock in both languages follows...

With a Python decorator:

def no_reentrancy(to_wrap):
  def wrapped(self, *args, **kwargs):
    if self._locked is True:
      raise Exception(...)
    self._locked = True

    return_val = to_wrap(self, *args, **kwargs)

    self._locked = False
    return return_val
  return wrapped

class MyClass:
  @no_reentrancy
  def sensitive(self, ...):
    ...

With a Solidity function modifier:

contract Mutex {
    bool locked;
    modifier noReentrancy() {
        require(
            !locked,
            "Reentrant call."
        );
        locked = true;
        _;
        locked = false;
    }

    /// This function is protected by a mutex, which means that
    /// reentrant calls from within `msg.sender.call` cannot call `f` again.
    /// The `return 7` statement assigns 7 to the return value but still
    /// executes the statement `locked = false` in the modifier.
    function f() public noReentrancy returns (uint) {
        require(msg.sender.call());
        return 7;
    }
}
  • 1
    That makes great sense. Thank you for the quick response. As an aside, what do you recommend is the best way to really learn and understand decorators? I understand how to implement the built-ins, as well as what they do, but when it comes to making a custom decorator, I find it difficult to do. Do you have good resources for this? – user45378 Aug 29 '18 at 19:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy