Reputation: 10383
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?
Upvotes: 7
Views: 1739
Reputation: 27023
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.
Upvotes: 9