-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Currently, defn takes the form of:
(defn <fn-name> <fn-args> [<return-type>] <decorator(s)> &<body>)
Where:
fn-argsis a list, potentially emptyreturn-typeis a single element or tuple literal, and is optionaldecorator(s)is either a single keyword, or list of keywordsbodyis one or more expressions
Some examples:
(defn __init__ [] :external
(setv self/greet "Hello World"))
(defn multiply [:uint256 x y] :uint256 [:external :pure]
(* x y))
(defn addAndSub [:uint256 x y] '(:uint256 :uint256) [:external :pure]
'((+ x y) (- x y)))I think having the decorators so far from the function name isn't great. This leads to the first line (declaration line, before the body) to grow pretty long, but breaking it up into multiple lines can get confusing as these elements start to look like they're part of the function body
Some options are implementing decorators as functions
(external
(defn __init__ []
(setv self/greet "Hello World")))This could in theory allow multiple functions to be defined under the same external block. This might not be a bad pattern, having all the external functions together, but decorators like :view or :pure would still have to go in the defn form, or the nesting will get too ugly
(external
(defn __init__ []
(setv self/greet "Hello World"))
(defn multiply [:uint256 x y] :uint256 :pure
(* x y)))The gains here are not insignificant. we separate the concept of "accessibility decorators" (:external, :internal) from "state modifiers" (:pure, :view). We'll lump in :payable with the "state decorators", mainly because if a function is :payable then it can't be :pure or :view, so we'll never have to have a list of decorators.
Then, we wouldn't really need :internal either if we just set functions to be internal by default (i.e. internal unless explicitly marked external)
New defn specification
A valid defn form would then be:
(defn <fn-name> <fn-args> [<return-type>] [<state-decorator>] &<body>)
fn-name,fn-args, andreturn-typeare unchanged- `state-decorator is now an optional single keyword, never a list
- function is internal by default
- we pass the function definition to
externalwhen we want to make the function accessible externally
Implementation as a macro
The coolest thing about this is we can make this change in a backwards-compatible way by just writing an external macro.
This macro does nothing more than take any defn forms passed to it, add an :external decorator according to the current syntax, and output that form.
The road to public
In solidity, a function can be marked public, which makes it accessible both internally and externally.
We can also easily implement public, but not as a macro, because it would require processing code outside of the macro body. But the flow would go like this:
- Declare an internal function with the function body and a prepend the name with an underscore
- Replace internal invocations of the function with the underscore-prepended invocation
- Declare an external function which passes through to the internal function
such that:
(public
(defn foo [] :uint256 :view
(+ 3 7)))
(defn internal-user [] :uint256
(- (self.foo) 2))is transformed into:
(defn _foo [] :uint256 :view
(+ 3 7))
(defn internal-user [] :uint256
(- (self._foo) 2))
(external
(defn foo [] :uint256 :view
(self._foo)))