----------------------------------------------------------------------------- -- | -- Module : Diagrams.Util -- Copyright : (c) 2011 diagrams-lib team (see LICENSE) -- License : BSD-style (see LICENSE) -- Maintainer : diagrams-discuss@googlegroups.com -- -- Some miscellaneous utilities provided by the diagrams-lib package. -- ----------------------------------------------------------------------------- module Diagrams.Util ( -- * Utilities for users with , applyAll , (#) , iterateN , tau -- * Internal utilities , foldB ) where import Data.Default.Class import Data.Monoid -- | Several functions exported by the diagrams library take a number -- of arguments giving the user control to \"tweak\" various aspects -- of their behavior. Rather than give such functions a long list -- of arguments, and to make it possible for the user to selectively -- override only certain arguments and use default values for -- others, such sets of arguments are collected into a record with -- named fields (see 'PolygonOpts' in "Diagrams.TwoD.Shapes" for an -- example). Such record types are made instances of the 'Default' -- class, which provides a single record structure ('def') -- collecting the \"default\" arguments to the function. @with@ is -- a synonym for 'def', which provides nice-looking syntax for -- simulating optional, named arguments in Haskell. For example, -- -- @ -- polygon with {sides = 7, edgeSkip = 2} -- @ -- -- calls the 'polygon' function with a single argument (note that -- record update binds more tightly than function application!), -- namely, 'with' (the record of default arguments) where the -- @sides@ and @edgeSkip@ fields have been updated. with :: Default d => d with = def -- | @applyAll@ takes a list of functions and applies them all to a -- value, in sequence from the last function in the list to the first. -- For example, @applyAll [f1, f2, f3] a == f1 . f2 . f3 $ a@. applyAll :: [a -> a] -> a -> a applyAll = appEndo . mconcat . map Endo infixl 8 # -- | Postfix function application, for conveniently applying -- attributes. Unlike @($)@, @(#)@ has a high precedence (8), so @d -- \# foo \# bar@ can be combined with other things using operators -- like @(|||)@ or @(\<\>)@ without needing parentheses. (#) :: a -> (a -> b) -> b (#) = flip ($) -- | @iterateN n f x@ returns the list of the first @n@ iterates of -- @f@ starting at @x@, that is, the list @[x, f x, f (f x), ...]@ -- of length @n@. (Note that the last element of the list will be -- @f@ applied to @x@ @(n-1)@ times.) iterateN :: Int -> (a -> a) -> a -> [a] iterateN n f = take n . iterate f -- | The circle constant, the ratio of a circle's circumference to its -- /radius/. Note that @pi = tau/2@. -- -- For more information and a well-reasoned argument why we should -- all be using tau instead of pi, see /The Tau Manifesto/, -- . -- -- To hear what it sounds like (and to easily memorize the first 30 -- digits or so), try . tau :: Floating a => a tau = 2*pi -- | Given an associative binary operation and a default value to use -- in the case of an empty list, perform a /balanced/ fold over a -- list. For example, -- -- @ -- foldB (+) z [a,b,c,d,e,f] == ((a+b) + (c+d)) + (e+f) -- @ -- foldB :: (a -> a -> a) -> a -> [a] -> a foldB _ z [] = z foldB f _ as = foldB' as where foldB' [x] = x foldB' xs = foldB' (go xs) go [] = [] go [x] = [x] go (x1:x2:xs) = f x1 x2 : go xs