packages feed

monad-ideals-0.1.0.0: src/Data/List/NotOne.hs

{-# LANGUAGE DeriveTraversable #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Data.List.NotOne
-- Copyright   :  (C) 2024 Koji Miyazato
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  Koji Miyazato <viercc@gmail.com>
-- Stability   :  experimental
module Data.List.NotOne where

import Data.Maybe (mapMaybe)
import Data.Foldable (toList)

import Data.Functor.Bind
import Data.Functor.Plus (Alt(..), Plus(..))
import Control.Monad.Isolated

import Data.List.TwoOrMore

-- | List sans singleton
data NotOne a = Zero | Multiple (TwoOrMore a)
  deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)

notOne :: [a] -> Either a (NotOne a)
notOne [] = Right Zero
notOne [a] = Left a
notOne (a1 : a2 : as) = Right . Multiple $ TwoOrMore a1 a2 as

getMultiple :: NotOne a -> Maybe (TwoOrMore a)
getMultiple Zero = Nothing
getMultiple (Multiple as) = Just as

instance Semigroup (NotOne a) where
  Zero <> bs = bs
  Multiple (TwoOrMore a1 a2 as) <> bs = Multiple $ TwoOrMore a1 a2 (as ++ toList bs)

instance Monoid (NotOne a) where
  mempty = Zero

instance Apply NotOne where
  Zero <.> _ = Zero
  Multiple _ <.> Zero = Zero
  Multiple as <.> Multiple bs = Multiple (as <.> bs)

instance Alt NotOne where
  (<!>) = (<>)

instance Plus NotOne where
  zero = mempty

-- | @(>>-) = flip foldMap@
instance Bind NotOne where
  Zero >>- _ = Zero
  Multiple as >>- k = case mapMaybe (getMultiple . k) (toList as) of
    [] -> Zero
    [bs] -> Multiple bs
    bs1 : bs2 : bss -> Multiple $ join (TwoOrMore bs1 bs2 bss)

instance Isolated NotOne where
  impureBind as k = Unite . notOne $ toList as >>= toList . k