{-# LANGUAGE DataKinds #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE OverloadedLabels #-}
-- | Labels for fields in a tuple.
--
-- Enable these extensions:
--
-- In GHCi:
--
-- @
-- :set -XOverloadedLabels -XTypeOperators -XDataKinds -XFlexibleContexts
-- @
--
-- In a module:
--
-- @
-- {-\# LANGUAGE OverloadedLabels, TypeOperators, DataKinds, FlexibleContexts \#-}
-- @
--
-- Construct a record:
--
-- >>> (#foo := "hi", #bar := 123)
-- (#foo := "hi",#bar := 123)
--
-- Get fields of a record:
--
-- >>> get #bar (#foo := "hi", #bar := 123)
-- 123
--
-- Set fields of a record:
--
-- >>> set #bar 66 (#foo := "hi", #bar := 123)
-- (#foo := "hi",#bar := 66)
--
-- Modify fields of a record:
--
-- >>> modify #mu (*0.1) (#bar := "hi", #mu := 123)
-- (#bar := "hi",#mu := 12.3)
--
-- Add fields to a record:
--
-- >>> cons (#mu := [1,2,3]) (#foo := "hi", #bar := 123)
-- (#mu := [1,2,3],#foo := "hi",#bar := 123)
--
-- Abstraction:
--
-- >>> let double field record = set field (get field record * 2) record
-- >>> double #mu (#bar := "hi", #mu := 123)
-- (#bar := "hi",#mu := 246)
--
-- Lenses:
--
-- >>> over (lens #sub . lens #foo) (*2) (#bar := "hello", #sub := (#foo := 123))
-- (#bar := "hello",#sub := #foo := 246)
--
-- Projection:
--
-- >>> project (#bar := "hello", #foo := 3, #mu := "hi") :: ("bar" := String, "foo" := Int)
-- (#bar := "hello",#foo := 3)
--
-- Field order projection:
--
-- >>> project (#bar := "hello", #foo := 3) :: ("foo" := Int, "bar" := String)
-- (#foo := 3,#bar := "hello")
--
-- Reflection of labels
--
-- >>> labels (#bar := "hello", #foo := 3, #mu := "hi")
-- ["bar","foo","mu"]
--
-- Reflection of labelled fields
--
-- >>> reflect @Show show (#bar := "hello", #foo := 3, #mu := "hi")
-- [("bar","\"hello\""),("foo","3"),("mu","\"hi\"")]
--

module Labels
-- Field access
  ( get
  , set
  , modify
  , lens
  , cons
  , project
  , reflect
  , labels
   -- Construction
  , (:=)(..)
  , Has
  , Cons
  , Project
  , Reflect
  , Subset
  , Labels)
  where

import Data.Proxy
import GHC.TypeLits
import Labels.Internal

type Lens s t a b = forall f. Functor f => (a -> f b) -> (s -> f t)

-- | Make a lens out of the label. Example: @over (lens #salary) (* 1.1) employee@
lens
  :: Has label value record
  => Proxy (label :: Symbol) -> Lens record record value value
lens label f record =
  fmap (\value -> set label value record) (f (get label record))
{-# INLINE lens #-}

-- | Strictly modify a field by doing: @modify #salary (* 1.1) employee@
modify :: Has label value record => Proxy label -> (value -> value) -> record -> record
modify f g r = set f (g v) r
  where !v = get f r
{-# INLINE modify #-}