Declarative Clarity

#1

Although Clarity generally supports declarative programming, the code examples resort to imperative programming with statements mutating the state of the contract as side effect.

Would it be possible for Clarity to support a declarative programming style also for functions that determine the next state of the contract?

It can be beneficial to avoid or at least restrict side effects by keeping most functions pure. The Welcome to Clarity guide even recommends a naming convention calling out functions with side effects:

Functions that mutate data by convention terminate with an ! exclamation point.

Yet the Clarity tutorial defines functions that mutate the contract state as side effect:

(define-map tokens ((account principal)) ((balance int)))

(define-private (token-credit! (account principal) (amount int))
  (if (<= amount 0)
      (err "must move positive balance")
      (let ((current-amount (get-balance account)))
        (begin
          (map-set! tokens (tuple (account account))
                      (tuple (balance (+ amount current-amount))))
          (ok amount)))))

(define-public (mint! (amount int))
   (let ((balance (get-balance tx-sender)))
     (token-credit! tx-sender amount)))

In this case, token-credit! mutates the persistent tokens map as a side effect. How could this functionality be implemented in Clarity using a declarative style?

1 Like
#2

I’m supportive of more declarative features in Clarity, but I’m not sure in this case how best to implement things like token transfers or contract data updates in general in a more declarative style. One approach would be to allow contracts to define something like transformation functions, which do not themselves mutate any state, but instead return transforms, which are then applied to the contract state. I’m not sure if it would offer much benefit for contract developers and I’d need to see some examples of it in use to be convinced.

2 Likes
#3

A declarative style could make it harder for developers to get into trouble, while facilitating static analysis. I’ll work on some examples.

How does Clarity deal with side effects in an input function to map? As in extending the earlier example:

(define-private (credit-sender! (amount int))
  (token-credit! tx-sender amount))

(define-public (map-side-effects!)
   (map credit-sender! (list 1 2 3 4 5)))
1 Like
#4

Any guidance on where to find quality tutorials or documentation that can help with writing Clarity contracts? You seem to have a strong grasp & it’d be beautiful to open up access to such pivotal know how :slight_smile: Thank you kindly for whatever possible. :pray:t5::fist:t5:

3 Likes
#5

How does Clarity deal with side effects in an input function to map ? As in extending the earlier example …

It allows mutation to occur in map functions. Since map, filter, fold are the only methods of iteration in Clarity, mutation in those functions is the only mechanism for iteratively applying mutations.

1 Like
#6

A variation is functions returning the transform as compound data, applied to the contract state by the caller. Updating the example above to this approach, the private function could return the transform, leaving to the public function to mutate the contract state:

(define-map tokens ((account principal)) ((balance int)))

(define-private (credit-token (account principal) (amount int))
  (if (<= amount 0)
    (err "must move positive balance")
    (let ((balance (get-balance account)))
      (ok { account: account,
            balance: (+ balance amount) }))))

(define-public (mint! (amount int))
  (let ((transformation (credit-token tx-sender amount)))
    (if (is-ok transformation)
      (map-insert tokens transformation)
      (err "Failed to mint"))))

This approach is simplified by built-in functions like map-insert taking an argument that combines the key and value, so the return value from functions generating the transformation doesn’t have to be destructured before making the call to mutate the contract. Avoiding mutating side effects in lower-level functions benefits static analysis.