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.
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) -> ...