How can I use the overloaded recording fields with lenses?

You can mix classes with lenses to simulate overloaded recording fields, down to the point. See, for example, makeFields in Control.Lens.TH . I'm trying to figure out if there is a good way to reuse the same name as the lens for some types, and a workaround for others. It is noteworthy that, taking into account the amount of products, each product may have lenses that deteriorate before going around the amount. The simplest thing I could think of: **:

First try

 class Boo booey where type Con booey :: (* -> *) -> Constraint boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey 

This works great for simple things like

 data Boop = Boop Int Char instance Boo Boop where type Con Boop = Functor boo f (Boop ic) = (\i' -> Boop i' c) <$> fi 

But he falls on his face as soon as you need something more complex, for example

 instance Boo boopy => Boo (Maybe boopy) where 

which should be able to create Traversal , regardless of the choice of base Boo .

Second attempt

The next thing I tried, what types of work is to restrict the Con family. It gets rude. First change the class:

 class LTEApplicative c where lteApplicative :: Applicative a :- ca class LTEApplicative (Con booey) => Boo booey where type Con booey :: (* -> *) -> Constraint boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey 

This causes the Boo instances to contain clear evidence that their Boo creates a Traversal' booey Int . A few more things:

 instance LTEApplicative Applicative where lteApplicative = Sub Dict instance LTEApplicative Functor where lteApplicative = Sub Dict -- flub :: Boo booey => Traversal booey booey Int Int flub :: forall booey f . (Boo booey, Applicative f) => (Int -> f Int) -> booey -> f booey flub = case lteApplicative of Sub (Dict :: Dict (Con booey f)) -> boo instance Boo boopy => Boo (Maybe boopy) where type Con (Maybe boopy) = Applicative boo _ Nothing = pure Nothing boo f (Just x) = Just <$> hum fx where hum :: Traversal' boopy Int hum = flub 

And the basic Boop example works unchanged.

Why is it still sucking

Now we have Boo , creating Lens or Traversal under the appropriate circumstances, and we can always use it as Traversal , but every time we want to do this, we must first drag that this is really one. This, of course, is too inconvenient to implement overloaded record fields! Is there a better way?

** This code compiles with the following (may not be minimal):

 {-# LANGUAGE PolyKinds, TypeFamilies, TypeOperators, FlexibleContexts, ScopedTypeVariables, RankNTypes, KindSignatures #-} import Control.Lens import Data.Constraint 
+5
source share
1 answer

For me, this worked before:

 {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} import Control.Lens data Boop = Boop Int Char deriving (Show) class HasBoo fs where boo :: LensLike' fs Int instance Functor f => HasBoo f Boop where boo f (Boop ab) = flip Boop b <$> fa instance (Applicative f, HasBoo fs) => HasBoo f (Maybe s) where boo = traverse . boo 

It can also be scaled to polymorphic fields if we ensure that all the corresponding functional dependencies are fulfilled (just like here ). Leaving an overloaded field completely polymorphic is rarely a useful or good idea; I will illustrate this case, although because from there one can always monomorphize as necessary (or we can restrict polymorphic fields, for example, the name field, to IsString ).

 {-# LANGUAGE UndecidableInstances, MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, TemplateHaskell #-} import Control.Lens data Foo ab = Foo {_fooFieldA :: a, _fooFieldB :: b} deriving Show makeLenses ''Foo class HasFieldA fstab | s -> a, t -> b, sb -> t, ta -> s where fieldA :: LensLike fstab instance Functor f => HasFieldA f (Foo ab) (Foo a' b) aa' where fieldA = fooFieldA instance (Applicative f, HasFieldA fstab) => HasFieldA f (Maybe s) (Maybe t) ab where fieldA = traverse . fieldA 

You can also figure out a little and use one class for all the "eat" functions:

 {-# LANGUAGE UndecidableInstances, MultiParamTypeClasses, RankNTypes, TypeFamilies, DataKinds, FlexibleInstances, FunctionalDependencies, TemplateHaskell #-} import Control.Lens hiding (has) import GHC.TypeLits import Data.Proxy class Has (sym :: Symbol) fstab | s sym -> a, sym t -> b, sb -> t, ta -> s where has' :: Proxy sym -> LensLike fstab data Foo a = Foo {_fooFieldA :: a, _fooFieldB :: Int} deriving Show makeLenses ''Foo instance Functor f => Has "fieldA" f (Foo a) (Foo a') aa' where has' _ = fooFieldA 

With GHC 8 you can add

 {-# LANGUAGE TypeApplications #-} 

and avoid proxies:

 has :: forall (sym :: Symbol) fsta b. Has sym fstab => LensLike fstab has = has' (Proxy :: Proxy sym) instance (Applicative f, Has "fieldA" fstab) => Has "fieldA" f (Maybe s) (Maybe t) ab where has' _ = traverse . has @"fieldA" 

Examples:

 > Just (Foo 0 1) ^? has @"fieldA" Just 0 > Foo 0 1 & has @"fieldA" +~ 10 Foo {_fooFieldA = 10, _fooFieldB = 1} 
+3
source

Source: https://habr.com/ru/post/1241339/


All Articles