{-# LANGUAGE UnicodeSyntax, MultiParamTypeClasses, FlexibleInstances #-}
-- | The multi-parameter type-class 'View' provides an abstraction @View v n@ of a type @n@ that exposes a value of type @v@. It allows both to 'inspect' and 'update' the value, while hiding the internal structure of the value type (@n@).
module Data.View where

-- TODO: Better?
--class Has v n where
--	inspect ∷ n → v
--
--class Has v n ⇒ View v n where

-- | Minimal complete definition: @inspect@ and one of {@update@, @adjust@}
class View v n where
	inspect  n  v
	update   v  n  n
	adjust   (v  v)  n  n

	update v
v = forall v n. View v n => (v -> v) -> n -> n
adjust (forall a b. a -> b -> a
const v
v)
	adjust v -> v
f n
n = forall v n. View v n => v -> n -> n
update (v -> v
f forall a b. (a -> b) -> a -> b
$ forall v n. View v n => n -> v
inspect n
n) n
n

instance View n n where
	inspect :: n -> n
inspect = forall n. n -> n
id
	update :: n -> n -> n
update = forall a b. a -> b -> a
const

instance (View v1 n, View v2 n)  View (v1,v2) n where
	inspect :: n -> (v1, v2)
inspect n
n = (forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n)
	update :: (v1, v2) -> n -> n
update (v1
v1,v2
v2) = forall v n. View v n => v -> n -> n
update v1
v1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v2
v2

instance (View v1 n, View v2 n, View v3 n)  View (v1,v2,v3) n where
	inspect :: n -> (v1, v2, v3)
inspect n
n = (forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n)
	update :: (v1, v2, v3) -> n -> n
update (v1
v1,v2
v2,v3
v3) = forall v n. View v n => v -> n -> n
update v1
v1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v2
v2 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v3
v3

instance (View v1 n, View v2 n, View v3 n, View v4 n)  View (v1,v2,v3,v4) n where
	inspect :: n -> (v1, v2, v3, v4)
inspect n
n = (forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n, forall v n. View v n => n -> v
inspect n
n)
	update :: (v1, v2, v3, v4) -> n -> n
update (v1
v1,v2
v2,v3
v3,v4
v4) = forall v n. View v n => v -> n -> n
update v1
v1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v2
v2 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v3
v3 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => v -> n -> n
update v4
v4

-- | convenience function that can be used to access record fields of the exposed type
examine  View v n  (v  field)  n  field
examine :: forall v n field. View v n => (v -> field) -> n -> field
examine v -> field
field = v -> field
field forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v n. View v n => n -> v
inspect

adjustM  (Monad m, View v n)  (v  m v)  n  m n
adjustM :: forall (m :: * -> *) v n.
(Monad m, View v n) =>
(v -> m v) -> n -> m n
adjustM v -> m v
f n
n = do
	v
n'  v -> m v
f forall a b. (a -> b) -> a -> b
$ forall v n. View v n => n -> v
inspect n
n
	forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall v n. View v n => v -> n -> n
update v
n' n
n