-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.Pointless.Lenses.Examples.MapExamples
-- Copyright   :  (c) 2010 University of Minho
-- License     :  BSD3
--
-- Maintainer  :  hpacheco@di.uminho.pt
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Pointless Lenses:
-- bidirectional lenses with point-free programming
-- 
-- More example involving composed maps.
--
-----------------------------------------------------------------------------

module Generics.Pointless.Lenses.Examples.MapExamples where

import Generics.Pointless.Functors
import Generics.Pointless.Combinators
import Generics.Pointless.Lenses
import Generics.Pointless.Lenses.Combinators
import Generics.Pointless.Lenses.Examples.Recs

-- ** map ! . filter_left . map (map f -|- map g)

mapbang_hand :: Lens [Either [(Int,Char)] [(Bool,Char)]] [One]
mapbang_hand = Lens get' put' create'
    where
    get' [] = []
    get' (Left _ :xs) = _L : get' xs
    get' (Right _:xs) = get' xs
    create' [] = []
    create' (x:xs) = Left [] : create' xs
    put' ([],[]) = []
    put' (l,Right y:ys) = Right y : put' (l,ys)
    put' ([],_) = []
    put' (x:xs,Left y:ys) = Left y : put' (xs,ys)
    put' (x:xs,[]) = Left [] : put' (xs,[])

mapbang_pf :: Lens [Either [(Int,Char)] [(Bool,Char)]] [One]
mapbang_pf = map_pf ((!<) (innList . inl . bang)) .< filter_left_pf
     .< map_pf (map_pf (fst_lns (pnt 'c' . bang)) -|-< map_pf (snd_lns (pnt True . bang)))
 
mapbang_opt :: Lens [Either [(Int,Char)] [(Bool,Char)]] [One]   
mapbang_opt = cataList_lns (((\/<) (inl . bang) f g) .< coassocl_lns .< (id_lns -|-< distl_lns))
    where f = innList_lns .< (id_lns -|-< (((!<) (innList . inl)) ><< id_lns))
          g = snd_lns _L
          
-- ** Persons (count the number of women)

type Person = (Name,Gender)
type Name = String
data Gender = M | F deriving (Eq,Show,Read)

innGender :: Either One One -> Gender
innGender = const M \/ const F

outGender :: Gender -> Either One One
outGender M = Left _L
outGender F = Right _L

outGender_lns :: Lens Gender (Either One One)
outGender_lns = Lens outGender (innGender . fst) innGender

type instance PF Gender = Const One :+: Const One
instance Mu Gender where
    inn (Left _) = M
    inn (Right _) = F
    out M = Left _L
    out F = Right _L

women_hand :: Lens [Person] Nat
women_hand = Lens get' put' create'
    where
    get' [] = Zero
    get' ((nm,M):ps) = get' ps
    get' ((nm,F):ps) = Succ (get' ps)
    create' Zero = []
    create' (Succ n) = ("woman",F) : create' n
    put' (Zero,[]) = []
    put' (n,(nm,M):ps) = (nm,M) : put' (n,ps)
    put' (Zero,_) = []
    put' (Succ n,[]) = ("woman",F) : create' n
    put' (Succ n,(nm,F):ps) = (nm,F) : put' (n,ps)

women_pf :: Lens [Person] Nat
women_pf = length_pf _L .< filter_right_pf .< map_pf (outGender_lns .< snd_lns (pnt "woman" . bang))
    
women_opt :: Lens [Person] Nat
women_opt = cataList_lns ((innNat_lns .< (id_lns -|-< snd_lns bang) .\/< snd_lns bang) .< f)
    where f = coassocl_lns
           .< (id_lns -|-< (coswap_lns .< distl_lns .< (g ><< id_lns)))
          g = outGender_lns .< snd_lns (pnt "woman" . bang)