Given an example data type with record syntax:
data VmInfo = VmInfo {infoVid :: String
,infoIndex :: Int
,infoPid :: Int
,infoExe :: String
} deriving (Show)
and (vmInfo :: String -> VmInfo) function that generates and returns the above data structure given vm name as string.
I can see two methods to extract the individual parts of the VmInfo data type.
(VmInfo vid _ _ _) <- vmInfo vm
Which is just a pattern match. And ...
vid <- infoVid <$> vmInfo vm
using record syntax compiler generated functions.
The question is simple: which is a preferred method?
Amount-of-typing wise they are the same so I am looking for speed and correctness/best practice.
I assume the pattern matching would be faster but then what is the point of record syntax?
Pattern matching comes up in several places in OCaml: as a powerful control structure combining a multi-armed conditional, unification, data destructuring and variable binding; as a shortcut way of defining functions by case analysis; and as a way of handling exceptions.
Overview. We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.
The pattern h :: t matches any non-empty list, calls the head of the list h (one element, the first one), and the tail of the list t (zero or more elements after the first one). The operator :: is the list constructor (often called "cons"), which is why these patterns match lists.
These aren't semantically equivalent.
Let's look at the first example:
(VmInfo vid _ _ _) <- vmInfo vm
This performs a pattern match in the binding operation. There are two results of this. The first is that the constructor of the result of the vmInfo vm
action is evaluated. This means that if vmInfo
ended with a line like return undefined
, the exception thrown by evaluating undefined
would happen at this pattern match, not a later use of vid
. The second is that if the pattern match is refuted (the pattern match does not match the value), the monad's fail
instance will be called with the pattern match error text. That's not possible in this case, but it is generally possible when pattern matching a constructor in a bind.
Now, on to the next example:
vid <- infoVid <$> vmInfo vm
By the definition of <$>
, this will be entirely lazy in the value returned by the action (not the effects). If vmInfo
ended with return undefined
, you wouldn't get the exception from evaluating undefined
until you did something that used the value of vid
. Additionally, if infoVoid
had the ability to throw any exceptions, they wouldn't end up happening until the use of vid
, best case.
Interestingly enough, these differences are only present in the scope of a monadic bind. If vmInfo
was pure and you were binding the name vid
inside a let
or where
expression, they would generate identical code.
In that case, which one you would want to use is entirely up to you. Both are idiomatic Haskell. People generally pick whichever looks better in the context they're working in.
The main reasons people use accessor functions is brevity when the record has so many fields a pattern match is huge, and because they are actual functions - they can be passed to any higher-order function their type fits into. You can't pass around pattern matches as a distinct construct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With