module FP.Prelude.Lens where

import FP.Prelude.Core
import FP.Prelude.Morphism

newtype Lens a b = Lens {runLens  a  (b,b  a)}
data Prism a b = Prism {construct  b  a,view  a  Maybe b}

-- # Cursors (common for Lens and Prism)

class Alter (t      ) where alter  t a b  (b  b)  a  a
class AlterM (t      ) where alterM  (Monad m)  t a b  (b  m b)  a  m a

update  (Alter t)  t a b  b  a  a
update 𝓁 x = alter 𝓁 $ const x

updateM  (AlterM t,Monad m)  t a b  m b  a  m a
updateM 𝓁 xM = alterM 𝓁 $ const xM

-- ## Lens

instance Category Lens where
  refl = isoLens id id
  Lens g  Lens f = Lens $ \ a 
    let (b,ba) = f a
        (c,cb) = g b
    in (c,ba  cb)
instance Alter Lens where
  alter l f a = let (b,ba) = runLens l a in ba $ f b
instance AlterM Lens where
  alterM l f a = let (b,ba) = runLens l a in map ba $ f b

lens  (a  b)  (a  b  a)  Lens a b
lens getter setter = Lens $ \ s  (getter s,setter s)

isoLens  (a  b)  (b  a)  Lens a b
isoLens to from = lens to $ const from

access  Lens a b  a  b
access = fst  runLens

-- ## Prism

instance Category Prism where
  refl = isoPrism id id
  g  f = Prism
    { view = view g * view f
    , construct = construct f  construct g
    }
instance Alter Prism where
  alter p f a = elimMaybe a (construct p  f) $ view p a

prism  (b  a)  (a  Maybe b)  Prism a b
prism = Prism

isoPrism  (b  a)  (a  b)  Prism a b
isoPrism from to = prism from $ Just  to

unsafeView  Prism a b  a  b
unsafeView = elimMaybe (error "unsafeView") id  view

shape  Prism a b  a  𝔹
shape = elimMaybe False (const True)  view

finiteL  Prism ℕᵀ 
finiteL = Prism NFinite $ \case
  NFinite x  Just x
  _  Nothing

leftL  Prism (a  b) a
leftL = Prism Left $ elimSum Just $ const Nothing

rightR  Prism (a  b) b
rightR = Prism Right $ elimSum (const Nothing) Just

fstL  Lens (a,b) a
fstL = lens fst $ \ (_,b)  (,b)

sndL  Lens (a,b) b
sndL = lens snd $ \ (a,_)  (a,)

nothingL  Prism (Maybe a) ()
nothingL = prism (const Nothing) $ elimMaybe (Just ()) $ const Nothing

justL  Prism (Maybe a) a
justL = Prism Just id

singleL  Prism [a] a
singleL = Prism single $ \case
  [x]  Just x
  _  Nothing