Do we have addition assignment(+=, -=, etc) operator in DAML?
Example:
let a= 10.0, b= 20.0
a+=b (or) a= a+b
When I tried above example DAML is not allowing the syntax and throwing recursive variable declaration error.
I have looked into the documentation but no luck. If we have addition assignment capability, could someone please point me to the right documentation.
Hi @Sneha, values in DAML are always immutable. So addition assignment doesnât exist because you can never mutate a value and add to it.
You might think that the following example mutates a value:
test = do
let a = 10.0
let a = 11
However, you are not mutating a. The third line in this example will create a new value called a which shadows (hides) the exsiting a defined in line 2.
In the binding let a = a + b, the a on the right of the = refers to the a on the left of the =. This is so that the reference behavior of functions and variables, whether in let or at the top-level, is unified; consider this to be like
let a () = a () + b
except that you get the error at compile-time.
Itâs generally worth the debate whether you ought to have two forms of let instead, one with the current behavior and one with the nonrecursive behavior. However, we must choose the confusion we want:
having two lets
you must use a fresh variable name each time a replacement is desired
There is more confusion in (1) than it might seem: even if we had nonrecursive let, it would only introduce a new binding, as @cocreature mentions, not alter the previous binding, so any code that referred to the prior binding would not see the new binding. So to extend his example, if you wrote
letnonrec a = 10.0
letnonrec f x = a + x
letnonrec a = a + 1
f 0
this would evaluate to 10, not 11.
You may use any convention you like for fresh variable names, but a common one is to add '. So you might write let a' = a + b. If we consider the above example written in this style, it is far more obvious that the result will be 10:
Okay I get this. Let me try once.
I am trying to achieve below scenario wherein variable a scope should be the out of for loop and capable to use it inside for.
let a = 0
forA quantity list(quantity => do
a = a + quantity)
There is a good chance that forA is not the function you want to use here. Thatâs because the function you pass to it is only allowed to depend on the argument (quantity), and the only form of âsequenceâ, or things happening in a particular order, comes from the actions you are performing with the do. Specifically, variable binding is not such an action; it is purely a local alias for a value, and cannot escape its scope of definition. (To put it another way, DAML functions must be true functions, whose only âeffectâ is in accepting arguments and returning values.)
You might be comfortable with forA from examples. However, it is not a âone size fits allâ function for working across the aggregate of a list; it is used in those examples because it hides some details of lists that are sometimes useful, but not always. If there is any âone size fits allâ function for lists, itâs foldr or foldl as @cocreatureused previously, but you wonât see foldr in most examples, because it is much nicer in DAML to use a function that more closely fits your use case, and foldr is far too powerful and general-purpose for that.
To determine what the proper function to use instead of forA is, first decide: are you performing any actions from the function youâre passing in, such as contract exercises or creates? If you are, a good starting point is the DA.Action module; if you are not, check the DA.Foldable module instead. Regardless, the starting point is to make that determination.
I also want to use this opportunity to plug the excellent Functional Programming 101 section in our documentation @bernhard added recently. In particular, the section around looping.
The generic looping construct over a list in DAML is foldl. You can think of it as walking down the list from left to right and, on each element, doing some operation while carrying over the result. The general form is:
foldl function_on_each_element initial_value list
where function_on_each_element is a function of two arguments, the accumulated value so far and the new element weâre considering, and must return the new accumulated value. This raises the question of what the âaccumulated value so farâ is when considering the first element, and that is precisely what the initial_value is there for. The final return value of foldl is the accumulated value after processing the last element.
where \soFar newElem -> soFar + newElem is a function that represents what to do on each step (quantity => do a = a + quantity) and 0 is the initial value (Let a = 0).
Note that in this case we can use a syntactic trick to make this shorter. This is completely optional and up to you whether you think this makes your code better. Here is the trick. First, variable names in a function donât really matter outside of it, so \soFar newElem -> soFar + newElem is really the same as \a b -> a + b. This is really just addition, though. In DAML, a binary operator like + can be turned into a function by wrapping it in parentheses, so (+) is exactly the same as \a b -> a + b. This means we can also write:
foldl (+) 0 quantityList
Finally, for this case the standard library also happens to have a builtin function to directly do what you want: sum quantityList.
In case that this wasnât just a proxy example and you really want to only add up a list of numbers, thereâs a utility function in the stdlib for that purpose:
I ran into an issue with a similar example and the compiler gives me the following error message:
⢠Couldn't match expected type âUpdate a0â with actual type âIntâ
⢠In a stmt of a 'do' block:
foldl
(\ quantity a -> quantity + a)
0
newlist
In the expression:
foldl
(\ quantity a -> quantity + a)
0
newlist
In my example âaâ is defined as a âDecimalâ type
Itâs a bit hard to tell for sure without more context on the surrounding code, but hereâs a guess: the code
foldl (\quantity a -> quantity + a) 0 newlist
is inferred to be of type Int (which seems reasonable for a sum), but the context in which it appears would have expected an expression of type Update a. The easiest way to transform a value into an Update wrapping that value is to use the function pure, so perhaps changing your code to:
pure $ foldl (\quantity a -> quantity + a) 0 newlist
will work.
Note that return and pure are exactly the same function; which one you use depends entirely on your personal preferences.
Also note that (\quantity a -> quantity + a) is semanticaly equivalent to (+), but may be slower depending on implementation details (Iâm not vey familiar with the internals of the compiler here; @Sofia_Faro is probably best placed to opine).