nom-0.1.0.0: src/Language/Nominal/Utilities.hs
{-|
Module : Utilities
Description : Helper functions
Copyright : (c) Murdoch J. Gabbay, 2020
License : GPL-3
Maintainer : murdoch.gabbay@gmail.com
Stability : experimental
Portability : POSIX
Helper functions
-}
{-# LANGUAGE FlexibleInstances
, InstanceSigs
, MultiParamTypeClasses
, FlexibleContexts
, TupleSections
, PartialTypeSignatures
#-}
module Language.Nominal.Utilities
where
import Data.Foldable (Foldable (..)) -- for toList
import Data.Generics
import Data.List
import Data.List.NonEmpty (NonEmpty (..))
import GHC.Stack (HasCallStack)
import Control.Monad (guard)
rewrite :: (Typeable a, Data b) => (a -> Maybe a) -> b -> b
rewrite f b = case cast b of
Just a
| Just (Just a') <- cast $ f a -> a'
_ -> gmapT (rewrite f) b
-- | Apply f repeatedly until we reach a fixedpoint
repeatedly :: Eq a => (a -> a) -> a -> a
repeatedly f x = let fx = f x in if fx == x then x else repeatedly f fx
-- | Apply a list of functions in succession.
chain :: [a -> a] -> a -> a
chain = foldr (.) id
-- https://hackage.haskell.org/package/speculate-0.4.1/docs/src/Test.Speculate.Utils.List.html#chain
-- | Standard function, returns @Just a@ provided @True@, and @Nothing@ otherwise
toMaybe :: Bool -> a -> Maybe a
toMaybe b a = guard b >> return a
-- | List subset. Surely this must be in a library somewhere.
isSubsetOf :: Eq a => [a] -> [a] -> Bool
isSubsetOf l1 l2 = all (`elem` l2) l1
-- | Interleave a list of lists to a list
interleave :: [[a]] -> [a]
interleave = concat . transpose
-- | Returns 'Just' the tail of a list if it can, and 'Nothing' otherwise.
safeTail :: [a] -> Maybe [a]
safeTail (_ : xs) = Just xs
safeTail _ = Nothing
-- | Returns 'Just' the head of a list if it can, and 'Nothing' otherwise.
safeHead :: [a] -> Maybe a
safeHead (h : _) = Just h
safeHead _ = Nothing
-- https://hackage.haskell.org/package/intro-0.7.0.0/docs/Intro.html#v:.:
-- | Compose functions with one argument with function with two arguments.
--
-- @f .: g = \\x y -> f (g x y)@.
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)
infixr 8 .:
{-# INLINE (.:) #-}
-- | Finds the unique element in a collection satisfying a predicate.
-- Results in an error if there is no such element or if there are more than one.
--
-- >>> iota (== 1) [1, 2, 3]
-- 1
--
-- >>> iota (> 1) [1, 2, 3]
-- *** Exception: iota: expected exactly one element to satisfy the predicate, but found at least two
-- ...
--
-- >>> iota (> 3) [1, 2, 3]
-- *** Exception: iota: expected exactly one element to satisfy the predicate, but found none
-- ...
--
iota :: (HasCallStack, Foldable f) => (a -> Bool) -> f a -> a
iota p xs = case filter p $ toList xs of
[x] -> x
[] -> error "iota: expected exactly one element to satisfy the predicate, but found none"
(_ : _) -> error "iota: expected exactly one element to satisfy the predicate, but found at least two"
-- | point moves the (first) element satisfying the predicate to the head of the list.
-- Error raised if no such element found.
--
-- >>> point even [1,2,3]
-- 2 :| [1,3]
--
-- >>> point even [1,3,5]
-- *** Exception: point: no such element
-- ...
--
point :: Foldable f => (a -> Bool) -> f a -> NonEmpty a
point p = go . toList
where
go (x : xs)
| p x = x :| xs
| otherwise = let (y :| ys) = go xs in y :| x : ys
go [] = error "point: no such element"