{-# 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 = adjust (const v)
	adjust f n = update (f $ inspect n) n

instance View n n where
	inspect = id
	update = const

instance (View v1 n, View v2 n)  View (v1,v2) n where
	inspect n = (inspect n, inspect n)
	update (v1,v2) = update v1 . update v2

instance (View v1 n, View v2 n, View v3 n)  View (v1,v2,v3) n where
	inspect n = (inspect n, inspect n, inspect n)
	update (v1,v2,v3) = update v1 . update v2 . update v3

instance (View v1 n, View v2 n, View v3 n, View v4 n)  View (v1,v2,v3,v4) n where
	inspect n = (inspect n, inspect n, inspect n, inspect n)
	update (v1,v2,v3,v4) = update v1 . update v2 . update v3 . update v4

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

adjustM  (Monad m, View v n)  (v  m v)  n  m n
adjustM f n = do
	n'  f $ inspect n
	return $ update n' n