How do I access fields of deeply nested tagged records?

Consider a simple example:

> data V2 a = V2 { x :: a, y :: a }
> v2 = V2 { x: V2 { x: 1, y: 2 }, y: V2 { x: 3, y: 4 } }

Suppose I want to extract the 1. What I tried:

> (x <<< x) v2
Error found: Unknown value x
> v2.x.x
Error found: Could not match type Record with type V2

It seems that I need to unwrap the V2 data constructor. A pattern matching lambda can do that, but I am looking for a more concise solution. There should be a simple way to do this, no?

1 Like

You might want to change your V2 definition to use a type alias. Your current definition is doing unnecessary boxing:

type V2 a = { x :: a, y :: a }
v2 = { x: { x: 1, y: 2}, y: {x: 3, y: 4}}
v2.x.x

If you want more type safety, you should wrap it in a newtype:

newtype V2 a = { x :: a, y :: a }
derive instance newtypeV2 :: Newtype (V2 a) _
v2 = V2 { x: V2 { x: 1, y: 2}, y: V2 {x: 3, y: 4}}
(_.x <<< un V2 <<< _.x <<< un V2) v2

un is from import Data.Newtype (un)

1 Like

I think you may want to use a newtype there instead of data. Then you can do this:

newtype V2 a = V2 { x :: a, y :: a }

derive instance newtypeV2 :: Newtype (V2 a) _

foo = (unwrap $ V2 { x: 1, y: 2}).x

Where Newtype is from Data.Newtype.

Edit: derive the instance like @JordanMartinez did.

1 Like

@JordanMartinez You beat me to it!

1 Like

Also, your code doesn’t compile. The newtype declaration doesn’t have a constructor and it should be Newtype (V2 a) _.

1 Like

Whoops! Thanks for the correction. I’ve updated my code above.

2 Likes

I don’t recommend using this, but for fun a wrote a class which unwraps nested newtypes of records like yours

Note that it doesn’t unwrap any/all newtypes in the data, just the ones that are the same type constructor as the top level newtype

1 Like