Sure, thanks:
The example comes from this concrete piece of source code: src/contracts/coop_charta.aes · master · Lexon Foundation / Lexon Coop Charta · GitLab
See the readme, on what it is for: README.md · master · Lexon Foundation / Lexon Coop Charta · GitLab
But as an example I think it is generic. The init function duplicates some of the functionality of the entry point create_membership
.
stateful entrypoint create_membership(_member : address, _owned_shares : int) =
let membership = { owned_shares = _owned_shares }
put(state{ membership_map[_member] = membership })
put(state{ total_shares = state.total_shares + state.member_s_initial_number_of_shares })
the part of the init
that duplicates it, in an enforced copy-paste style of programming is:
let founder_s_initial_number_of_shares = _founder_s_initial_number_of_shares
...
{ ...
total_shares = _founder_s_initial_number_of_shares,
...
membership_map = { [founder] = {owned_shares=founder_s_initial_number_of_shares} }
This is very baked in and the preferred way that is impossible because if how init
and the state
record work would be as follows.
Below is a cleaned-up example, so that this thread will not be derailed by inefficiencies in our code that are intended but irrelevant for this discussion.
public stateful entrypoint init(_founder_s_initial_number_of_shares : int, _application_fee : int, _member_s_initial_number_of_shares : int) =
let founder = Call.caller
let director = founder
put (state{ founder = founder,
director = director,
application_fee = _application_fee,
member_s_initial_number_of_shares = _member_s_initial_number_of_shares,
total_shares = 0,
founder_s_initial_number_of_shares = _founder_s_initial_number_of_shares,
application_map = {},
membership_map = {} )
create_membership(founder, founder_s_initial_number_of_shares) }
My main point, again, is that being unable to use certain functions that are stateful in init while not having another constructor that is always executed at contract creation is unduly limiting and will lead to either duplication of code, i.e. avoidable errors, and/or contracts that are deployed and not fully initialized. Which latter will need the added overhead to check that before anything else can be called they reach the fully initialized state, which will clutter the code and add error potential. The validity of this concern can be seen from the optimization where you do not actually store the init code on-chain but throw it away. I am talking about MORE code that should be thrown away, too, because it is only used once in the very beginning. Just it can’t because it had to call stateful functions.
The example above gets around all that by duplicating functionality of a stateful function. What I lose is the clarity of setting 0 rap {} the initial state. Instead I put stuff right in. This is correct and optimized but less clear than I would prefer. Which also points to this being a design flaw.
I understand that implementation concerns might make my hole point mute. I can imagine how and why the limitation exists. But with these things the intuition would be if I run up against it this way, maybe it is not serving the app programmers well enough staying to close to implementation purity.