{- |
Module : $Header$
Description : Auxiliary functions
Copyright : (c) 2001 - 2003 Wolfgang Lux
2011 - 2015 Björn Peemöler
2016 - 2017 Finn Teegen
License : BSD-3-clause
Maintainer : fte@informatik.uni-kiel.de
Stability : experimental
Portability : portable
The module Utils provides a few simple functions that are
commonly used in the compiler, but not implemented in the Haskell
Prelude or standard library.
-}
module Base.Utils
( fst3, snd3, thd3, curry3, uncurry3
, (++!), foldr2, mapAccumM, findDouble, findMultiples
) where
import Control.Monad (MonadPlus, mzero, mplus)
import Data.List (partition)
infixr 5 ++!
-- The Prelude does not contain standard functions for triples.
-- We provide projection, (un-)currying, and mapping for triples here.
fst3 :: (a, b, c) -> a
fst3 (x, _, _) = x
snd3 :: (a, b, c) -> b
snd3 (_, y, _) = y
thd3 :: (a, b, c) -> c
thd3 (_, _, z) = z
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 f x y z = f (x,y,z)
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (x, y, z) = f x y z
-- The function (++!) is variant of the list concatenation operator (++)
-- that ignores the second argument if the first is a non-empty list.
-- When lists are used to encode non-determinism in Haskell,
-- this operator has the same effect as the cut operator in Prolog,
-- hence the ! in the name.
(++!) :: [a] -> [a] -> [a]
xs ++! ys = if null xs then ys else xs
-- Fold operations with two arguments lists can be defined using
-- zip and foldl or foldr, resp. Our definitions are unfolded for
-- efficiency reasons.
-- foldl2 :: (a -> b -> c -> a) -> a -> [b] -> [c] -> a
-- foldl2 _ z [] _ = z
-- foldl2 _ z _ [] = z
-- foldl2 f z (x : xs) (y : ys) = foldl2 f (f z x y) xs ys
foldr2 :: (a -> b -> c -> c) -> c -> [a] -> [b] -> c
foldr2 _ z [] _ = z
foldr2 _ z _ [] = z
foldr2 f z (x : xs) (y : ys) = f x y (foldr2 f z xs ys)
mapAccumM :: (Monad m, MonadPlus p) => (acc -> x -> m (acc, y)) -> acc -> [x]
-> m (acc, p y)
mapAccumM _ z [] = return (z, mzero)
mapAccumM f z (x:xs) = do
(z', y) <- f z x
(z'', ys) <- mapAccumM f z' xs
return (z'', return y `mplus` ys)
-- The function 'findDouble' checks whether a list of entities is linear,
-- i.e., if every entity in the list occurs only once. If it is non-linear,
-- the first offending object is returned.
findDouble :: Eq a => [a] -> Maybe a
findDouble [] = Nothing
findDouble (x : xs)
| x `elem` xs = Just x
| otherwise = findDouble xs
findMultiples :: Eq a => [a] -> [[a]]
findMultiples [] = []
findMultiples (x : xs)
| null same = multiples
| otherwise = (x : same) : multiples
where (same, other) = partition (==x) xs
multiples = findMultiples other