first-and-last-0.1.0.1: src/Data/Monoid/First.hs
{-# 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 <markandrusroberts@gmail.com>
-- 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