They are not semantically equivalent.
Let's look at the first example:
(VmInfo vid _ _ _) <- vmInfo vm
Performs pattern matching in the binding operation. These are two results. First, the constructor of the result of the vmInfo vm
action is vmInfo vm
. This means that if vmInfo
ended with a string of type return undefined
, an exception caused by evaluating undefined
will occur when this pattern matches, and not in the future use of vid
. Secondly, if a pattern match is refuted (the pattern match does not match the value), the monad fail
instance is called with the pattern matching error text. This is not possible in this case, but it is generally possible when the template matches the constructor in bind.
Now, to the following example:
vid <- infoVid <$> vmInfo vm
By definition, <$>
, this will be completely lazy in the value returned by the action (and not the effects). If vmInfo
ended with return undefined
, you would not get an exception from the undefined
evaluation until you did what used the vid
value. In addition, if infoVoid
was able to throw any exceptions, they would not end before using vid
, the best case.
Interestingly, these differences are present only within the framework of monadic binding. If vmInfo
was clean and you linked the vid
name inside a let
or where
expression, they would generate identical code.
In this case, the one you want to use is completely up to you. Both are idiomatic Haskell. People usually choose what looks best in the context in which they work.
The main reasons people use access functions is brevity, when so many fields in a record have a huge coincidence of patterns, and because they are actual functions, they can be passed to any higher order function into which their type fits. You cannot pass pattern matches as a separate construct.