module Control.Lens.Getter

import Data.Bicontravariant
import Data.Profunctor
import Data.Profunctor.Costrong
import Control.Lens.Optic
import Control.Lens.Indexed
import Control.Lens.Lens

%default total


------------------------------------------------------------------------------
-- Type definitions
------------------------------------------------------------------------------


public export
record IsGetter p where
  constructor MkIsGetter
  runIsGetter : (Strong p, Bicontravariant p)

export %hint
getterToLens : IsGetter p => IsLens p
getterToLens @{MkIsGetter _} = MkIsLens %search

export %hint
indexedGetter : IsGetter p => IsGetter (Indexed i p)
indexedGetter @{MkIsGetter _} = MkIsGetter %search


||| A getter is an optic that only supports getting, not setting.
|||
||| `Getter s a` is equivalent to a simple function `s -> a`.
public export
0 Getter : (s,a : Type) -> Type
Getter = Simple (Optic IsGetter)

||| An indexed getter returns an index while getting.
public export
0 IndexedGetter : (i,s,a : Type) -> Type
IndexedGetter = Simple . IndexedOptic IsGetter


------------------------------------------------------------------------------
-- Utilities for getters
------------------------------------------------------------------------------


||| Construct a getter from a function.
public export
to : (s -> a) -> Getter s a
to f @{MkIsGetter _} = lmap f . rphantom

||| Construct an indexed getter from a function.
public export
ito : (s -> (i, a)) -> IndexedGetter i s a
ito f @{MkIsGetter _} @{ind} = lmap f . rphantom . indexed @{ind}

||| Construct a getter that always returns a constant value.
public export
like : a -> Getter s a
like = to . const


||| Access the value of an optic and apply a function to it, returning the result.
public export
views : Getter s a -> (a -> r) -> s -> r
views l = runForget . l . MkForget

||| Access the focus value of an optic, particularly a `Getter`.
public export
view : Getter s a -> s -> a
view l = views l id

||| Access the value and index of an optic and apply a function to them,
||| returning the result.
public export
iviews : IndexedGetter i s a -> (i -> a -> r) -> s -> r
iviews l = runForget . l @{%search} @{Idxed} . MkForget . uncurry

||| Access the focus value and index of an optic, particularly a `Getter`.
public export
iview : IndexedGetter i s a -> s -> (i, a)
iview l = runForget $ l @{%search} @{Idxed} $ MkForget id


infixl 8 ^.
infixl 8 ^@.

||| Access the focus value of an optic, particularly a `Getter`.
|||
||| This is the operator form of `view`.
public export
(^.) : s -> Getter s a -> a
(^.) x l = view l x

||| Access the focus value and index of an optic, particularly a `Getter`.
|||
||| This is the operator form of `iview`.
public export
(^@.) : s -> IndexedGetter i s a -> (i, a)
(^@.) x l = iview l x