-- Copyright 2016 Google Inc. All Rights Reserved. -- -- Use of this source code is governed by a BSD-style -- license that can be found in the LICENSE file or at -- https://developers.google.com/open-source/licenses/bsd {-# LANGUAGE DataKinds #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} -- | A simple interface supporting overloaded record fields. -- -- An example instance: -- -- > data Foo = Foo { _bar :: Bool } -- > type instance Field "bar" Foo = Bool -- > -- > instance HasField "bar" Foo Foo where -- > field _ = lens _bar (\f x -> f { _bar = x }) -- > -- > bar :: HasField "bar" a a => Lens a b Bool Bool -- > bar = field (ProxySym :: ProxySym "bar") -- -- The proto compiler defines separate instances of 'HasField' for each field -- of each record. It defines the accessors (e.g., the lens @bar@ above) once -- per unique field name, shared between different message types within the -- same module. module Data.ProtoLens.Field ( -- * Overloaded fields Field , HasField(..) , ProxySym(..) -- * Helper lenses , maybeLens ) where import Data.Maybe (fromMaybe) import GHC.TypeLits (Symbol) import Lens.Family2 (Lens, Lens') import Lens.Family2.Unchecked (lens) -- | A \"proxy\" value for a type-level string (i.e., a type of kind 'Symbol'). data ProxySym (s :: Symbol) = ProxySym -- | The type of the field 's' in a message of type 'a'. type family Field (s :: Symbol) a -- | A class for overloaded fields. -- -- - 's' is a 'Symbol' representing the field name. -- - 'a' is the message type for getting this field -- - 'b' is the message type after setting this field. -- -- Currently 'a' and 'b' are the same in all generated instances. In the -- future, when we support type-checking of required fields we'll need -- different 'a' and 'b'; for more details see . class HasField (s :: Symbol) a b | a -> b, b -> a where field :: ProxySym s -> Lens a b (Field s a) (Field s b) -- | A helper lens for accessing optional fields. -- This is used as part of code generation, and should generally not be needed -- explicitly. -- -- Note that 'maybeLens' does not satisfy the lens laws, which expect that @set -- l (view l x) == x@. For example, -- -- > set (maybeLens 'a') (view (maybeLens 'a') Nothing) == Just 'a' -- -- However, this is the behavior generally expected by users, and only matters -- if we're explicitly checking whether a field is set. maybeLens :: b -> Lens' (Maybe b) b maybeLens x = lens (fromMaybe x) $ const Just