Question

I have several data structures like

data Data1 = Data1
    { _data1Field :: Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data1

data Data2 = Data2
    { _data2Field :: Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data2

-- More similar data types

So I decided to write a simple type class to make it easier to compose

class HasField a where
    field :: Lens' a Int

instance HasField Data1 where
    field = data1Field

instance HasField Data2 where
    field = data2Field

But then I ran into the problem that some of these structures have the corresponding field as optional

data Data3 = Data3
    { _data3Field :: Maybe Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data3

And now I can no longer use the type class. Since there are about the same number of data types that have that field optional as not, I decided that it'd be better to change the typeclass:

class HasField a where
    field :: Lens' a (Maybe Int)

instance HasField Data3 where
    field = data3Field

But since I'm not very experienced with the lens library, I'm stuck figuring out how to make this new lens work with the types for Data1 and Data2. Ideally, I'd like to be able to view it and get a Maybe Int value for any type, and when setting I'd like Just x to set the field to x for Data1 and Data2 and be a no-op for those two types when passed Nothing.

Is this possible using existing combinators or am I going to have to write the lens myself? I'm fine doing so, but the majority of existing tutorials use TH and gloss over the details of writing one by hand.

I'm using GHC 7.6.3 and lens 3.10.

Était-ce utile?

La solution

As a follow up to shachaf

class HasFieldA d where
  field :: Traversal' d Int

instance HasFieldA Data1 where
  field = data1Field -- Lens's are Traversals

instance HasFieldA Data3 where
  field = data3Field . _Just

And then the ^? operator or the ^.. operator

getField :: HasFieldA d => d -> Maybe Int
getField = d ^? field -- or preview field d

to get it.

To set optional fields, you'd need another function

class SetFieldA d where
  setField :: Setter' d Int
instance SetFieldA Data3 where
  setField = set data3Field . Just
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top