How to override the default value through lenses only if the input value is nothing

Basically, I'm trying to override the default set of values โ€‹โ€‹in a record only if the values โ€‹โ€‹for the user are NOT Nothing . Can this be done through lenses?

 import qualified Data.Default as DD instance DD.Def Nouns where def = Nouns { -- default values for each field come here } lookupHStore :: HStoreList -> Text -> Maybe Text mkNounsFromHStoreList :: HStoreList -> Nouns mkNounsFromHStoreList h = (DD.def Nouns) & depSingular .~ (lookupHStore h "dep_label_singular") -- ERROR: Won't compile because Text and (Maybe Text) don't match 
+1
source share
4 answers

You can create your own combinator:

 (~?) :: ASetter' sa -> Maybe a -> s -> s s ~? Just a = s .~ a s ~? Nothing = id 

What you can use like .~ :

 mkNounsFromHStoreList :: HStoreList -> Nouns mkNounsFromHStoreList h = DD.def & myNoun1 ~? lookupHStore h "potato" & myNoun2 ~? lookupHStore h "cheese" 
+2
source

This is similar to Alternative . Maybe Alternative instance implements a left-biased choice - its <|> is selected by the first value not Nothing .

 import Control.Applicative import Data.Semigroup data Foo = Foo { bar :: Maybe Int, baz :: Maybe String } 

I am going to implement a Semigroup instance for Foo , which takes <|> points by field of the record. Thus, the operation x <> y overrides the y fields with the corresponding fields << 24> x . (You can also use the monoid First , it does the same.)

 instance Semigroup Foo where f1 <> f2 = Foo { bar = bar f1 <|> bar f2, baz = baz f1 <|> baz f2 } ghci> let defaultFoo = Foo { bar = Just 2, baz = Just "default" } ghci> let overrides = Foo { bar = Just 8, baz = Nothing } ghci> overrides <> defaultFoo Foo {bar = Just 8, baz = Just "default"} 

Note that you do not need lenses for this, although they can help you make a small copy (<>) .

When the user gives you a partially filled Foo , you can fill in the rest of the fields by adding Foo by default.

 fillInDefaults :: Foo -> Foo fillInDefaults = (<> defaultFoo) 

One fun thing you can do with this is the Maybe definition from Foo .

 {-# LANGUAGE RankNTypes #-} import Control.Applicative import Data.Semigroup import Data.Functor.Identity data Foo f = Foo { bar :: f Int, baz :: f String } 

Foo , which I originally wrote above, is now equivalent to Foo Maybe . But now you can express invariants such as โ€œthis Foo has all the fields filled inโ€ without duplicating Foo itself.

 type PartialFoo = Foo Maybe -- the old Foo type TotalFoo = Foo Identity -- a Foo with no missing values 

The Semigroup instance, which referred only to the Maybe instance of Alternative , remains unchanged,

 instance Alternative f => Semigroup (Foo f) where f1 <> f2 = Foo { bar = bar f1 <|> bar f2, baz = baz f1 <|> baz f2 } 

but now you can generalize defaultFoo to an arbitrary Applicative .

 defaultFoo :: Applicative f => Foo f defaultFoo = Foo { bar = pure 2, baz = pure "default" } 

Now, having a bit of Traversable inspired categorical nonsense,

 -- "higher order functors": functors from the category of endofunctors to the category of types class HFunctor t where hmap :: (forall x. fx -> gx) -> tf -> tg -- "higher order traversables", -- about which I have written a follow up question: https://stackoverflow.com/q/44187945/7951906 class HFunctor t => HTraversable t where htraverse :: Applicative g => (forall x. fx -> gx) -> tf -> g (t Identity) htraverse eta = hsequence . hmap eta hsequence :: Applicative f => tf -> f (t Identity) hsequence = htraverse id instance HFunctor Foo where hmap eta (Foo bar baz) = Foo (eta bar) (eta baz) instance HTraversable Foo where htraverse eta (Foo bar baz) = liftA2 Foo (Identity <$> eta bar) (Identity <$> eta baz) 

fillInDefaults can be customized to express the invariant that there are no values โ€‹โ€‹as a result of Foo .

 fillInDefaults :: Alternative f => Foo f -> f TotalFoo fillInDefaults = hsequence . (<> defaultFoo) -- fromJust (unsafely) asserts that there aren't -- any `Nothing`s in the output of `fillInDefaults` fillInDefaults' :: PartialFoo -> TotalFoo fillInDefaults' = fromJust . fillInDefaults 

Probably redundant than you need, but it's still pretty neat.

+2
source

Ok, I found a possible solution, but I'm still looking for the best!

 mkNounsFromHStoreList :: HStoreList -> Nouns mkNounsFromHStoreList h = (DD.def Nouns) & depSingular %~ (overrideIfJust (lookupHStore h "dep_label_singular")) -- and more fields come here... where overrideIfJust val x = maybe x id val 
+1
source

How about using fromMaybe instead of creating an instance of Default ?

EDIT . Since you seem to want to use Default for other purposes:

 ฮป > import Data.Default ฮป > import Data.Maybe ฮป > :t fromMaybe def fromMaybe def :: Default a => Maybe a -> a 

This seems to be what you need.

0
source

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


All Articles