I'm not sure if you would be happy about this answer as I'm also proposing a bit of refactoring here...
If you are not happy please let me know and I will try to provide code sample based on below lenses without touching your types ;-)
Are you able to accept changing AsString
and AsSize
as follows?
newtype Names a = Names { names :: Array a }
type AsString = Names String
type AsSize = Names Int
This refactoring will simplify operations a bit and makes your type a bit more reusable. I really recommend this talk about the power of type parameters: https://www.youtube.com/watch?v=BHjIl81HgfE).
For names
field we can create lens by using generic prop
function.
Regarding Name
type we should derive Newtype
instance at first (please notice quite specific syntax of this deriving - I think that _
type compiler deduces by itself):
newtype Names a = Names { names :: Array a }
derive instance newtypeNames ∷ Newtype (Names a) _
This class provides wrap
and unwrap
methods which are used by _Newtype
lens. So we can just use _Newtype
lens now.
Finally you should be able to compose these two. Here we go:
module Main where
import Prelude
import Data.Lens (over, traversed)
import Data.Lens.Iso.Newtype (_Newtype)
import Data.Lens.Record (prop)
import Data.Newtype (class Newtype)
import Data.String as String
import Type.Prelude (SProxy(..))
newtype Names a = Names { names :: Array a }
derive instance newtypeNames ∷ Newtype (Names a) _
type AsString = Names String
type AsSize = Names Int
_names = prop (SProxy ∷ SProxy "names")
toSizes :: AsString -> AsSize
toSizes = over (_Newtype <<< _names <<< traversed) (String.length)
P.S.
I often write also this to simplify type inference if I'm modifying the same type:
_Newtype' ∷ ∀ s a. (Newtype s a) ⇒ Iso' s a
_Newtype' = iso unwrap wrap