Adding a bit of context to make the last two lines less magical. First off, <$>
is the same function as fmap
, but defined as an infix operator. This means fmap a b
is exactly the same as a <$> b
. In the following I’ll talk only about fmap
, but obviously this applies just as well to <$>
.
fmap
is a function defined on the Functor
type class. As an early, informal, and incomplete explanation, you can think of a functor as some sort of container, and fmap function box
as applying a function function
to whatever is currently in the container box
, returning a new box containing the result. The details of how that works depend on the type of the container. Here are a few concrete examples:
$ daml repl
daml> succ 1
2
daml> succ 4
5
daml> fmap succ [1]
[2]
daml> fmap succ [1, 2, 3, 4]
[2,3,4,5]
daml> fmap succ (Some 4)
Some 5
daml> fmap succ (None : Optional Int)
None
daml>
As you can see, using fmap f
on a List
will return a new List of the same length where each element is the result of applying f
to the corresponding element in the original list. Using fmap f
on an Optional
results in a new Optional
that is None
(empty) if the original was None
, and a Some
with the value of applying the f
function if there was originally a value.
These could be defined as:
fmap : (a -> b) -> [a] -> [b]
fmap f [] = []
fmap f (x::xs) = f x :: fmap f xs
and
fmap : (a -> b) -> Optional a -> Optional b
fmap f None = None
fmap f (Some x) = Some (f x)
(These are not quite the correct syntax as fmap
is a typeclass method, not a function. See this page for an example of how to implement typeclass instances.)
Stretching the analogy a bit, you can also think of Update
as a container of sorts: it’s a magic box that will contain a result once the code has been run on the ledger. Despite the analogy not holding out too well here (it was informal and incomplete to begin with), Update
is also an instance of Functor
, and you can similarly think of fmap
as somehow sending the function inside the update to be applied there. How would you go about doing that? Well, first you would have to create a context in which you can run some code (do
), then in that context you can extract the value from the existing update (v <- update
), and then you can apply the function and wrap the result in a new Update
context (pure (f v)
). So in this case the implementation would look something like:
fmap : (a -> b) -> Update a -> Update b
fmap f u = do
v <- u
pure (f v)
which is pretty much exactly the code in the branches of your choice, where Left
(and Right
for the other branch) is the function you want to apply.
So the final code using fmap
would be:
module Main where
template ContractCreation
with
owner: Party
where
signatory owner
controller owner can
nonconsuming CreateContract: Either (ContractId Contract1) (ContractId Contract2)
with
creatorType: Text
do if (creatorType == "1") then fmap Left (create Contract1 with owner)
else if (creatorType == "2") then fmap Right (create Contract2 with owner)
else abort "Error"
template Contract1
with
owner: Party
where
signatory owner
template Contract2
with
owner: Party
where
signatory owner
As a final note, when I have a three-way (or more) branch, I tend to prefer pattern matching to if-then-else. This is very much a personal preference and there’s nothing wrong with if-then-else, but in case you’re curious here is what the body of the choice would look like using pattern matching (i.e. a case ... of
expression):
do case creatorType of
"1" -> fmap Left (create Contract1 with owner)
"2" -> fmap Right (create Contract2 with owner)
_ -> abort "Error"