return-type is a single element or tuple literal, and is optional
decorator(s) is either a single keyword, or list of keywords
body is 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
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)
`state-decorator is now an optional single keyword, never a list
function is internal by default
we pass the function definition to external when 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
Currently,
defn
takes the form of:(defn <fn-name> <fn-args> [<return-type>] <decorator(s)> &<body>)
Where:
fn-args
is a list, potentially emptyreturn-type
is a single element or tuple literal, and is optionaldecorator(s)
is either a single keyword, or list of keywordsbody
is one or more expressionsSome examples:
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
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 thedefn
form, or the nesting will get too uglyThe 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
specificationA valid
defn
form would then be:(defn <fn-name> <fn-args> [<return-type>] [<state-decorator>] &<body>)
fn-name
,fn-args
, andreturn-type
are unchangedexternal
when we want to make the function accessible externallyImplementation 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 anydefn
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:such that:
is transformed into: