{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ScopedTypeVariables #-} ------------------------------------------------------------------------------- -- | -- Module : Data.Monoid.First -- Copyright : (C) 2015 Mark Andrus Roberts -- License : BSD-style (see the file LICENSE) -- Maintainer : Mark Andrus Roberts -- Stability : provisional -- -- @'First'' n@ is a generalization of the @First@ exported by @Data.Monoid@: -- whereas @Data.Monoid.First@ returns up to one value, @'First'' n@ returns -- up to @n@ values. -- -- @ -- Data.Monoid.First a ≡ -- 'First'' 1 a ≡ -- 'First' a -- @ -- -- This library also provides an API-compatible type synonym 'First' and -- function 'getFirst' allowing you to use it as a drop-in replacement. ------------------------------------------------------------------------------- module Data.Monoid.First ( -- * @First@ First , getFirst -- * @First'@ , First' , getFirst' ) where import Control.Applicative (Applicative((<*>), pure), Alternative) import Data.Data (Data) import Data.Proxy (Proxy(Proxy)) import Data.String (IsString(fromString)) import GHC.Generics (Generic, Generic1) import GHC.TypeLits (KnownNat, Nat, natVal) import Prelude (($), (.), Char, Eq, Foldable(foldr), Functor, Maybe(Just, Nothing), Monoid(mappend, mempty), Ord, Read, Show, Traversable, fromIntegral, head, take) -- $setup -- >>> import Prelude -------------------------------------------------------------------------------- -- * First -------------------------------------------------------------------------------- -- | A type isomorphic to @Data.Monoid.First@ type First a = First' 1 a -- | Get the first value of type @a@, if any. -- -- >>> getFirst (foldMap pure []) -- Nothing -- -- >>> getFirst (foldMap pure [1,2,3,4]) -- Just 1 getFirst :: First a -> Maybe a getFirst (First' []) = Nothing getFirst (First' as) = Just . head $ take 1 as -------------------------------------------------------------------------------- -- * First' -------------------------------------------------------------------------------- -- | A generalized version of @Data.Monoid.First@ newtype First' (n :: Nat) a = First' { _getFirst' :: [a] } deriving ( Alternative , Data , Eq , Foldable , Functor , Generic , Generic1 , Ord , Read , Show , Traversable ) -- | Get the first @n@ values or fewer of type @a@. -- -- >>> getFirst' (foldMap pure [1,2,3,4] :: First' 0 Int) -- [] -- -- >>> getFirst' (foldMap pure [1,2,3,4] :: First' 1 Int) -- [1] -- -- >>> getFirst' (foldMap pure [1,2,3,4] :: First' 2 Int) -- [1,2] getFirst' :: First' n a -> [a] getFirst' = _getFirst' instance KnownNat n => Applicative (First' n) where First' l <*> First' r = First' $ l <*> r pure a = case natVal (Proxy :: Proxy n) of 0 -> mempty _ -> First' $ pure a instance KnownNat n => Monoid (First' n a) where First' l `mappend` First' r = let n = fromIntegral $ natVal (Proxy :: Proxy n) in First' . take n $ l `mappend` r mempty = First' mempty instance KnownNat n => IsString (First' n Char) where fromString = foldr (\c a -> pure c `mappend` a) mempty