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