Dasy is currently in pre-alpha. The language’s core is still being designed and implemented.
Dasy has a clojure-inspired lisp syntax with some influences from python. Some constructs are dasy-specific.
Most vyper code can be translated by wrapping in parentheses properly. For example, you can assume that for arr.append(10)
in Vyper, the equivalent Dasy is (.append arr 10)
Tuples are represented by a quoted list such as '(1 2 3)
The vyper equivalent is (1, 2, 3)
Arrays are represented by a bracketed list, such as [1 2 3]
The vyper equivalent is [1, 2, 3]
Dasy has all of Vyper’s types. Base types such as uint256
are represented with a dasy ‘keyword’, which uses a colon and an identifier. Complex types are represented with a function-call syntax. Arrays are created with array
, or dyn-array
for dynamic arrays.
Vyper | Dasy |
---|---|
uint256 | :uint256 |
bool | :bool |
bytes32 | :bytes32 |
String[10] | (string 10) |
uint256[10] | (array :uint256 10) |
HashMap[uint256, bool] | (hash-map :uint256 :bool) |
DynArray[uint256, 5] | (dyn-array :uint256 5) |
Dasy has mostly identical operators and builtins as Vyper. There are a few small differences.
Binary operations are chained by default in Dasy. This allows you to specify more than two arguments at at time.
Because of this, in Dasy, +
functions like a sum
operator.
Vyper | Dasy |
---|---|
2 + 3 + 4 + 5 | (+ 2 3 4 5) |
x < y and y < z and z < a | (< x y z a) |
(defn fn-name args [return-type] visibility & body)
This special form declares and defines a function within a smart contract.
The args
list may be an empty list, but must be present. Returning multiple values requires declaring the return type as a tuple.
The return-type
object is optional. If present, it may be a single keyword representing the return type, or it may be a tuple of keywords for returning multiple values.
The visibility
object may also be a keyword or list of keywords. Valid values are:
:external
:internal
:payable
:view
:pure
(nonreentrant "lock-name")
(defn noArgs [] :external (pass))
(defn setNum [:uint256 x] :external (pass))
(defn addNums [:uint256 x y] :uint256 [:external :pure]
(+ x y))
(defn addAndSub [:uint256 x y] '(:uint256 :uint256) [:external :pure]
'((+ x y) (- x y)))
(defvar variable-name type [value])
This special form declares and optionally assigns a value to a variable.
Outside of a defn
form, variables are stored in storage
and accessible via self.variable-name
.
Inside a defn
form, variables are stored in memory
and accessible directly.
The value
form is optional.
(defvar owner (public :address))
(defvar enabled :bool)
(defn foo [] :external
(defvar owner_memory :address self/owner)) ;; declare copy in memory
(set name value)
This special form assigns a value to a name. It is roughly equivalent to the equal sign =
in Vyper.
;; Create a string variable that can store maximum 100 characters
(defvar greet (public (string 100)))
(defn __init__ [] :external
(set self/greet "Hello World")) ;; in vyper: self.greet = "Hello World"
(definterface name & fns)
This special form declares an interface.
(definterface TestInterface
(defn owner [] :address :view)
(defn setOwner [:address owner] :nonpayable)
(defn sendEth [] :payable)
(defn setOwnerAndSendEth [:address owner] :payable))
(defstruct name & variables)
This special form declares a struct. Variables should be declared in pairs of name
and type
(defstruct Person
name (string 100)
age :uint256)
(defevent name & fields)
This special form declares an event. Fields should be declared in pairs of name
and type
(defevent Transfer
sender (indexed :address)
receiver (indexed :address)
amount :uint256)
(defconst name value)
This special form defines a constant. The value must be provided when defined. This value can never change.
(defconst MIN_AMT 100)
(defconst GREETING "Hello")
(defmacro name args & body)
This special form defines a macro. Macros are functions that run at compile time. Their inputs are code, and their outputs are code. They transform your code as it is built.
Macros can be used to implement convenient shorthand syntaxes. They can also be used to pull in information from the outside world into your contract at build time.
In the most simple terms, macros allow you to extend the Dasy compiler yourself in whichever way you see fit.
;; (set-at myArr 0 100) -> (set (subscript myArr 0) 100)
(defmacro set-at [array index new-val] `(set (subscript ~array ~index) ~new-val))
;; (doto obj (.append 10) (.append 20)) -> (do (.append obj 10) (.append obj 20))
(defmacro doto [ obj #*cmds]
(lfor c cmds
`(~(get c 0) ~obj ~@(cut c 1 None))))
(if test body else-body)
If executes a test, and depending on the result, either executes the body
or the else-body
.
(if (x < 100)
*** (return 1) (return 0))
for
loops can operate on arrays directly, or on a range
(for [i nums]
(+= sum i))
(for [i [1 2 3 4 5]]
(+= sum i))
(for [i (range 10)]
(+= sum i))
In a for
loop’s body, continue
and break
behave the same as they do in Vyper.
(for [i (range 10)]
(if (== i 5)
(continue))
(+= sum i))
assert
behaves as it does in Vyper. It expects a test and an optional error message.
(assert (< x 100) "x must be less than 100")
raise
behaves as it does in Vyper. It expects a message.
(if (>= x 100)
(raise "x must be less than 100"))
(set self/foo bar)
Access object attributes. obj/name
is shorthand for (. obj name)
(cond & body)
cond
saves you from having too many nested if/elses
(if (< x 100)
100
(if (< x 1000)
1000
(if (< x 10000)
10000)))
;; this is equivalent
(cond
(< x 100) 100
(< x 1000) 1000
(< x 10000) 10000)
(set-at obj index val)
Sets a value at an index within an object. This object can be an array, dynamic array, or hashmap.
This expands to (set (subscript array index) new-val)
The vyper equivalent looks like obj[index] = val
(defvar arr (array :uint256 10)
myMap (hash-map :addr :bool))
(set-at arr 0 100) ;; arr[0] = 100
(set-at myMap 0x1234.... True) ;; myMap[0x1234....] = True
(set-in obj attr val)
Sets the value of an attribute of an object. This object is usually a struct.
This expands to (set (. obj attr) val)
(defstruct Person
name (string 10)
age :uint256)
(defvar p Person)
(set-in p age 40)
(set-in p name "Vitalik")
(doto obj & body)
Call multiple functions on the same object. Allows for shorter code.
(doto obj (.foo 10) (.bar 100))
expands to (do (.foo obj 10) (.bar obj 100))
;; above example rewritten with doto
(defstruct Person
name (string 10)
age :uint256)
(doto p
(defvar Person)
(set-in age 40)
(set-in name "Vitalik"))