Read-only functions can be called by the contract itself, as well as from the outside. They can return any type, just like private functions.
As the name implies, read-only functions may only perform read operations. You can read from data variables and maps but you cannot write to them. Read-only functions can also be purely functional; that is to say, calculate some result based on an input and return it. This is fine:
(define-read-only (add (a uint) (b uint)) (+ a b) ) (print (add u5 u10))
And so is this:
(define-data-var counter uint u0) (define-read-only (get-counter-value) (var-get counter) ) (print (get-counter-value))
But this one not so much:
(define-data-var counter uint u0) (define-read-only (increment-counter) (var-set (+ (var-get counter) u1)) ) (print (increment-counter))
As you can see, the analysis tells us it detected a writing operation inside a read-only function:
Analysis error: expecting read-only statements, detected a writing operation (define-data-var counter uint u0)
Not to worry though, there is no way to mess that up. If analysis fails the contract is rendered invalid, which means it cannot be deployed on the network.
One thing that makes read-only functions very interesting is that they can be called without actually sending a transaction! By using read-only functions, you can read the contract state for your application without requiring your users to pay transaction fees. Stacks.js and the Web Wallet Extension have support for calling read-only functions built-in. You can try it yourself right now with the Stacks Sandbox. Find a contract with a read-only function and call it directly. Completely free!
(define-map counters principal uint) (map-set counters 'ST1J4G6RR643BCG8G8SR6M2D9Z9KXT2NJDRK3FBTK u5) (map-set counters 'ST20ATRN26N9P05V2F1RHFRV24X8C8M3W54E427B2 u10) (define-read-only (get-counter-of (who principal)) ;; Implement. ) ;; These exist: (print (get-counter-of 'ST1J4G6RR643BCG8G8SR6M2D9Z9KXT2NJDRK3FBTK)) (print (get-counter-of 'ST20ATRN26N9P05V2F1RHFRV24X8C8M3W54E427B2)) ;; This one does not: (print (get-counter-of 'ST21HMSJATHZ888PD0S0SSTWP4J61TCRJYEVQ0STB))