packages feed

primus-0.1.0.0: src/Primus/ZipNonEmpty.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

{- |
Module      : Primus.ZipNonEmpty
Description : 'Control.Applicative.ZipList' version for 'NonEmpty'
Copyright   : (c) Grant Weyburne, 2022
License     : BSD-3
-}
module Primus.ZipNonEmpty (
  ZipNonEmpty (..),
  _Zip1,
  unZipNonEmpty,
) where

import Control.Applicative
import Control.DeepSeq
import Data.Coerce
import Data.Data
import qualified Data.Functor.Apply as Apply
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as N
import Data.Semigroup.Foldable
import Data.Semigroup.Traversable
import qualified GHC.Exts as GE (IsList (..))
import GHC.Generics (Generic, Generic1)
import Primus.Error (normalError)
import Primus.Lens

-- | unwrap 'ZipNonEmpty'
unZipNonEmpty :: ZipNonEmpty a -> NonEmpty a
unZipNonEmpty = coerce

-- | zippable version of 'NonEmpty'
newtype ZipNonEmpty a = ZipNonEmpty (NonEmpty a)
  deriving stock (Data, Generic, Generic1, Show, Eq, Ord, Traversable, Read)
  deriving (NFData) via (NonEmpty a)
  deriving (NFData1, Foldable, Foldable1, Functor) via NonEmpty

-- checkers hangs
instance Monoid a => Monoid (ZipNonEmpty a) where
  mempty = pure mempty

instance Semigroup a => Semigroup (ZipNonEmpty a) where
  (<>) = liftA2 (<>)

instance Applicative ZipNonEmpty where
  pure = ZipNonEmpty . N.repeat
  liftA2 f (ZipNonEmpty xs) (ZipNonEmpty ys) = ZipNonEmpty (N.zipWith f xs ys)

instance Apply.Apply ZipNonEmpty where
  (<.>) = (<*>)

instance Traversable1 ZipNonEmpty where
  traverse1 afb = fmap ZipNonEmpty . traverse1 afb . unZipNonEmpty

instance GE.IsList (ZipNonEmpty a) where
  type Item (ZipNonEmpty a) = a
  fromList =
    \case
      [] -> normalError "IsList: fromList: need at least one element"
      x : xs -> ZipNonEmpty (x :| xs)
  toList = N.toList . coerce

-- | iso for the zipnonempty constructor
_Zip1 :: Iso (ZipNonEmpty a) (ZipNonEmpty b) (NonEmpty a) (NonEmpty b)
_Zip1 = iso coerce coerce