packages feed

casa-reoc-0.1.0.0: src/ReOC/Primitive/Some.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall #-}
-- someOne has a constraint (1 <= n) that its body does not use, by design.
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

{- | From 1 to @n@ values: as many as a part of a document has room for, such
as the rows of a table of CASA Form 101-02.

The constructor is not exported (see "ReOC.Primitive.Text1"). @Some@ is
built with the prism '_Some' or, for one value, with 'someOne'. It is
'Traversable', so that its values can be changed without changing how many
there are.
-}
module ReOC.Primitive.Some (
  Some,
  _Some,
  someNonEmpty,
  someOne,
) where

import Control.Lens (Prism', prism')
import Data.List.NonEmpty (NonEmpty (..))
import Data.Proxy (Proxy (..))
import GHC.TypeNats (KnownNat, Nat, natVal, type (<=))

{- $setup
>>> :set -XDataKinds
>>> :set -XTypeApplications
>>> import Control.Lens ((^?))
-}

-- | From 1 to @n@ values.
newtype Some (n :: Nat) a
  = Some (NonEmpty a)
  deriving (Eq, Ord, Show, Functor, Foldable, Traversable)

{- | From 1 to @n@ values.

>>> ('a' :| "bc") ^? _Some @3
Just (Some ('a' :| "bc"))

>>> ('a' :| "bcd") ^? _Some @3
Nothing
-}
_Some :: forall n a. (KnownNat n) => Prism' (NonEmpty a) (Some n a)
_Some =
  prism'
    someNonEmpty
    (\xs -> if fromIntegral (length xs) <= natVal (Proxy :: Proxy n) then Just (Some xs) else Nothing)

-- | The values.
someNonEmpty :: Some n a -> NonEmpty a
someNonEmpty (Some xs) =
  xs

-- | One value.
someOne :: (1 <= n) => a -> Some n a
someOne x =
  Some (x :| [])