Search code examples
haskellhaskell-lenslenses

data-class equivalent of `_1`-style tuple lens shortcut?


As per its documentation, Haskell's lens library's _1 provides a lens for tuples.

For data records, there are instead several other functions such as makeLenses, automatically generating lenses based on the record's field names.

Unfortunately, I am dealing with data classes without named fields, meaning makeLenses appears out for me. This makes me wonder. _1 seems quite convenient, yet as implied in its documentation, appears not to work on data classes. Is there an equivalent of a similar level of convenience?

> :set -package lens
> import Control.Lens
> (1,2) ^. _1
1
> data Bar = Bar String deriving Show
> bar = Bar "abc"
> bar ^. _1

<interactive>:271:1: error:
    • Non type-variable argument in the constraint: Field1 Bar Bar b b
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall b. Field1 Bar Bar b b => b

Adding the FlexibleContexts extension, I instead face another error:

bar ^. _1

<interactive>:6:1: error:
    • No instance for (Field1 Bar Bar () ()) arising from a use of ‘it’
    • In the first argument of ‘print’, namely ‘it’
      In a stmt of an interactive GHCi command: print it

Solution

  • Field1 has a default generic implementation, so you can probably add an instance yourself:

    {-# LANGUAGE DeriveGeneric #-}
    import GHC.Generics (Generic)
    
    data Bar = Bar String deriving (Show, Generic)
    
    instance Field1 Bar Bar String String
    

    generic-lens also provides the same functionality, without requiring that boilerplate instance. _1 is called position @1 (from Data.Generics.Product(.Positions)).