0

I have a function generalized over a function func to be applied on MyDataType1 and another MyDataType1 like this

setThis :: (Functor f0, Applicative f0) => Lens' MyDataType1 (f0 Float) -> Float -> MyDataType1 -> MyDataType1 -> MyDataType1         
setThis func toThis dataType1 dataType2 = dataType2 & func %~ (\a -> (+) <$> a <*> delta
    where baseStat = dataType1 ^. func -- baseStat has type Maybe Float
          delta = (\a -> toThis * a) <$> baseStat -- also have type Maybe Float

where MyDataType1 is (when used with print. Every numeric value is a Float)

data MyDataType1 = MyDataType1 { _name = Just "First"
                               , _length = Just 5.5 
                               , _dmsTypes = Just
                                     ( DMS { _i = Just 1.9
                                           , _j = Nothing
                                           , _k = Just 95.9
                                           }
                                      )
                               }

The function setThis given a default record function like length, a constant Float, a data set to get base value from and a data set to modify, sets _length to a number that's a sum of the original value and the value from the other set multiplied by some constant.

It works just as I expect when given function length.

What I want to do is have the exact same behavior when given a function like (dmsTypes . _j) as in

setThis (dmsTypes . _Just . _j) 0.3 (someY) (someY) -- someY :: MyDataType1 

Although GHC throws this error if I do just that

Could not deduce (Applicative f) arising from a use of ‘_Just’
  from the context: Functor f
    bound by a type expected by the context:
               Lens' MyDataType1 (Maybe Float)
Possible fix:
    add (Applicative f) to the context of
      a type expected by the context:
        Lens' MyDataType1 (Maybe Float)

And while it seems like GHC knows exactly what I should do, I don't know how to do it.

atis
  • 881
  • 5
  • 22
  • `setThis` expects a lens, i.e., that you can always set or get exactly one value. However, the optic `dmsTypes . _Just . _j` is not a lens, because there is nothing to set or get if the `_dmsTypes` field is `Nothing`. What would you like to do with this `Nothing`? – Li-yao Xia Jan 09 '19 at 15:30
  • @Li-yaoXia Oh! That makes a lot of sense. If `_dmsTypes` is `Nothing` I would want it propagated. So if `setThis` encounters `Nothing` in the set I get the data from, it should do nothing. The function should only 'set' something in the `dataType2` if `_dmsTypes` is `Just x`. – atis Jan 09 '19 at 15:48
  • Hm, does my answer do what you want if `_j` is `Nothing` in `dataType1` but `Just _` in `dataType2`, or the other way around? Or does it not matter? – Li-yao Xia Jan 09 '19 at 16:46
  • It does. Since I'm using applicative operators, the behaviour is similar to fmap on `Just` but simple addition on `Nothing` – atis Jan 09 '19 at 17:07

1 Answers1

2

Since the thing func points at might not exist, it should be a traversal (any number of focused values) rather than a lens (exactly one focused value).

The part of setThis that uses a setter (i.e., (%~)) remains the same, but the getter part (i.e., (^.)) should instead use a fold, with (^?). In that case, dataType1 ^? func will have two layers of Maybe, one from (^?) and one from func (in what is currently f0), that you'll probably want to flatten with join.

baseState = join (dataType1 ^? func)

Now f0 must be Maybe.

setThis :: Traversal' MyDataType1 (Maybe Float) -> ...
Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56