Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Haskell Record Update Seemingly Incompatible Type

I am trying to understand the internal code of haskell-skiplist.

The author defines the following algebraic data type and some relevant functions – the details of which do not really matter.

data Node k v
  = Node { nKey :: k
         , nValue :: v
         , nSibling :: Node k v
         , nChild :: Node k v }
  | Head { nSibling :: Node k v
         , nChild :: Node k v }
  | Nil

nodeLTKey :: forall k v. Ord k => Node k v -> k -> Bool
nodeEQKey :: forall k v. Eq k => Node k v -> k -> Bool
sibling :: forall k v. Node k v -> Node k v

Later in the insert function around line 113, they define a helper function of type Node k v -> (Node k v, Int). I have extracted the part I am confused about below.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

go Nil = (Nil, wins)
go n
  | nodeLTKey sN k = (n {nSibling = fst $ go sN}, -1)
  | nodeEQKey sN k = (n {nSibling = sN {nValue = v}}, -1) 
  -- how can they update the inner record?
  where
    sN = sibling n
    -- How can the compiler be sure that sN is a Node and not Head / Nil?

The sibling function returns a Node k v and only one of the possible variants of a Node k v actually has a field called nValue. I don’t understand how the second guard can be sure that sN will have an nValue field – i.e that sN will be of the Node case and not Head / Nil.

>Solution :

From just the types and code posted, indeed there is no guarantee that sN will be of the Node constructor, and the code could fail at runtime. But if we look at context you linked but did not copy, nodeEQKey is defined to only return True for Node objects whose field matches nodeEQKey‘s parameter. So the second equation can safely update its nValue, because its body will only be executed if its guard matches, which will only happen if sN is a Node with nKey == k.

The compiler does not guarantee this node will have the right shape; the check happens at runtime. But if you turn on -Wall, you will be warned of an incomplete pattern match, which could fail at runtime.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading