packages feed

safe 0.3.11 → 0.3.12

raw patch · 7 files changed

+102/−56 lines, 7 filesdep ~base

Dependency ranges changed: base

Files

CHANGES.txt view
@@ -1,5 +1,8 @@ Changelog for Safe +0.3.12+    #19, add Safe.Partial exposing a Partial typeclass+    #19, add support for GHC call stacks 0.3.11     #16, add Safe succ and pred     #16, add readEitherSafe for better errors than readEither
Safe.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds  #-} {- | A module wrapping @Prelude@/@Data.List@ functions that can throw exceptions, such as @head@ and @!!@. Each unsafe function has up to four variants, e.g. with @tail@:@@ -8,10 +10,13 @@  * @'tailDef' :: /[a]/ -> [a] -> [a]@, takes a default to return on errors. -* @'tailNote' :: /String/ -> [a] -> [a]@, takes an extra argument which supplements the error message.+* @'tailNote' :: 'Partial' => /String/ -> [a] -> [a]@, takes an extra argument which supplements the error message.  * @'tailSafe' :: [a] -> [a]@, returns some sensible default if possible, @[]@ in the case of @tail@. +All functions marked with the @'Partial'@ constraint are not total, and will produce stack traces on error, on GHC+versions which support them (see "GHC.Stack").+ This module also introduces some new functions, documented at the top of the module. -} @@ -49,14 +54,15 @@ import Safe.Util import Data.List import Data.Maybe+import Safe.Partial  --------------------------------------------------------------------- -- UTILITIES -fromNote :: String -> String -> Maybe a -> a+fromNote :: Partial => String -> String -> Maybe a -> a fromNote = fromNoteModule "Safe" -fromNoteEither :: String -> String -> Either String a -> a+fromNoteEither :: Partial => String -> String -> Either String a -> a fromNoteEither = fromNoteEitherModule "Safe"  @@ -66,7 +72,7 @@ -- | Synonym for 'error'. Used for instances where the program --   has decided to exit because of invalid user input, or the user pressed --   quit etc. This function allows 'error' to be reserved for programmer errors.-abort :: String -> a+abort :: Partial => String -> a abort = error  @@ -96,7 +102,7 @@ -- | -- > tailNote "help me" [] = error "Safe.tailNote [], help me" -- > tailNote "help me" [1,3,4] = [3,4]-tailNote :: String -> [a] -> [a]+tailNote :: Partial => String -> [a] -> [a] tailNote note = fromNote note "tailNote []" . tailMay  -- |@@ -112,7 +118,7 @@ initDef :: [a] -> [a] -> [a] initDef def = fromMaybe def . initMay -initNote :: String -> [a] -> [a]+initNote :: Partial => String -> [a] -> [a] initNote note = fromNote note "initNote []" . initMay  initSafe :: [a] -> [a]@@ -140,7 +146,7 @@ minimumDef def = fromMaybe def . minimumMay maximumDef def = fromMaybe def . maximumMay -minimumNote, maximumNote :: Ord a => String -> [a] -> a+minimumNote, maximumNote :: (Partial, Ord a) => String -> [a] -> a minimumNote note = fromNote note "minumumNote []" . minimumMay maximumNote note = fromNote note "maximumNote []" . maximumMay @@ -152,7 +158,7 @@ minimumByDef def = fromMaybe def .^ minimumByMay maximumByDef def = fromMaybe def .^ maximumByMay -minimumByNote, maximumByNote :: String -> (a -> a -> Ordering) -> [a] -> a+minimumByNote, maximumByNote :: Partial => String -> (a -> a -> Ordering) -> [a] -> a minimumByNote note = fromNote note "minumumByNote []" .^ minimumByMay maximumByNote note = fromNote note "maximumByNote []" .^ maximumByMay @@ -167,7 +173,7 @@ foldl1Def def = fromMaybe def .^ foldl1May foldl1Def' def = fromMaybe def .^ foldl1May' -foldr1Note, foldl1Note, foldl1Note' :: String -> (a -> a -> a) -> [a] -> a+foldr1Note, foldl1Note, foldl1Note' :: Partial => String -> (a -> a -> a) -> [a] -> a foldr1Note note = fromNote note "foldr1Note []" .^ foldr1May foldl1Note note = fromNote note "foldl1Note []" .^ foldl1May foldl1Note' note = fromNote note "foldl1Note []" .^ foldl1May'@@ -180,7 +186,7 @@ scanr1Def def = fromMaybe def .^ scanr1May scanl1Def def = fromMaybe def .^ scanl1May -scanr1Note, scanl1Note :: String -> (a -> a -> a) -> [a] -> [a]+scanr1Note, scanl1Note :: Partial => String -> (a -> a -> a) -> [a] -> [a] scanr1Note note = fromNote note "scanr1Note []" .^ scanr1May scanl1Note note = fromNote note "scanl1Note []" .^ scanl1May @@ -190,7 +196,7 @@ cycleDef :: [a] -> [a] -> [a] cycleDef def = fromMaybe def . cycleMay -cycleNote :: String -> [a] -> [a]+cycleNote :: Partial => String -> [a] -> [a] cycleNote note = fromNote note "cycleNote []" . cycleMay  -- | An alternative name for 'fromMaybe', to fit the naming scheme of this package.@@ -198,16 +204,16 @@ fromJustDef :: a -> Maybe a -> a fromJustDef  = fromMaybe -fromJustNote :: String -> Maybe a -> a+fromJustNote :: Partial => String -> Maybe a -> a fromJustNote note = fromNote note "fromJustNote Nothing" -assertNote :: String -> Bool -> a -> a+assertNote :: Partial => String -> Bool -> a -> a assertNote note True val = val assertNote note False val = fromNote note "assertNote False" Nothing   -- | Synonym for '!!', but includes more information in the error message.-at :: [a] -> Int -> a+at :: Partial => [a] -> Int -> a at = fromNoteEither "" "at" .^ at_  atMay :: [a] -> Int -> Maybe a@@ -216,7 +222,7 @@ atDef :: a -> [a] -> Int -> a atDef def = fromMaybe def .^ atMay -atNote :: String -> [a] -> Int -> a+atNote :: Partial => String -> [a] -> Int -> a atNote note = fromNoteEither note "atNote" .^ at_  -- | This function provides a more precise error message than 'readEither' from 'base'.@@ -237,7 +243,7 @@ readDef def = fromMaybe def . readMay  -- | 'readNote' uses 'readEitherSafe' for the error message.-readNote :: Read a => String -> String -> a+readNote :: (Partial, Read a) => String -> String -> a readNote note = fromNoteEither note "readNote" . readEitherSafe  -- |@@ -248,7 +254,7 @@ lookupJustDef :: Eq a => b -> a -> [(a,b)] -> b lookupJustDef def = fromMaybe def .^ lookup -lookupJustNote :: Eq a => String -> a -> [(a,b)] -> b+lookupJustNote :: (Partial, Eq a) => String -> a -> [(a,b)] -> b lookupJustNote note = fromNote note "lookupJustNote, no matching value" .^ lookup  -- |@@ -259,7 +265,7 @@ findJustDef :: a -> (a -> Bool) -> [a] -> a findJustDef def = fromMaybe def .^ find -findJustNote :: String -> (a -> Bool) -> [a] -> a+findJustNote :: Partial => String -> (a -> Bool) -> [a] -> a findJustNote note = fromNote note "findJustNote, no matching value" .^ find  -- |@@ -270,7 +276,7 @@ elemIndexJustDef :: Eq a => Int -> a -> [a] -> Int elemIndexJustDef def = fromMaybe def .^ elemIndex -elemIndexJustNote :: Eq a => String -> a -> [a] -> Int+elemIndexJustNote :: (Partial, Eq a) => String -> a -> [a] -> Int elemIndexJustNote note = fromNote note "elemIndexJustNote, no matching value" .^ elemIndex  -- |@@ -281,7 +287,7 @@ findIndexJustDef :: Int -> (a -> Bool) -> [a] -> Int findIndexJustDef def = fromMaybe def .^ findIndex -findIndexJustNote :: String -> (a -> Bool) -> [a] -> Int+findIndexJustNote :: Partial => String -> (a -> Bool) -> [a] -> Int findIndexJustNote note = fromNote note "findIndexJustNote, no matching value" .^ findIndex  -- From http://stackoverflow.com/questions/2743858/safe-and-polymorphic-toenum@@ -298,7 +304,7 @@ toEnumDef :: (Enum a, Bounded a) => a -> Int -> a toEnumDef def = fromMaybe def . toEnumMay -toEnumNote :: (Enum a, Bounded a) => String -> Int -> a+toEnumNote :: (Partial, Enum a, Bounded a) => String -> Int -> a toEnumNote note = fromNote note "toEnumNote, out of range" . toEnumMay  toEnumSafe :: (Enum a, Bounded a) => Int -> a@@ -310,7 +316,7 @@ succDef :: (Enum a, Eq a, Bounded a) => a -> a -> a succDef def = fromMaybe def . succMay -succNote :: (Enum a, Eq a, Bounded a) => String -> a -> a+succNote :: (Partial, Enum a, Eq a, Bounded a) => String -> a -> a succNote note = fromNote note "succNote, out of range" . succMay  succSafe :: (Enum a, Eq a, Bounded a) => a -> a@@ -322,7 +328,7 @@ predDef :: (Enum a, Eq a, Bounded a) => a -> a -> a predDef def = fromMaybe def . predMay -predNote :: (Enum a, Eq a, Bounded a) => String -> a -> a+predNote :: (Partial, Enum a, Eq a, Bounded a) => String -> a -> a predNote note = fromNote note "predNote, out of range" . predMay  predSafe :: (Enum a, Eq a, Bounded a) => a -> a
Safe/Exact.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds  #-} {- | Provides functions that raise errors in corner cases instead of returning \"best effort\" results, then provides wrappers like the "Safe" module. For example:@@ -33,11 +35,12 @@ import Control.Arrow import Data.Maybe import Safe.Util+import Safe.Partial  --------------------------------------------------------------------- -- HELPERS -addNote :: String -> String -> String -> a+addNote :: Partial => String -> String -> String -> a addNote note fun msg = error $     "Safe.Exact." ++ fun ++ ", " ++ msg ++ (if null note then "" else ", " ++ note) @@ -46,7 +49,7 @@ -- IMPLEMENTATIONS  {-# INLINE splitAtExact_ #-}-splitAtExact_ :: (String -> r) -> ([a] -> r) -> (a -> r -> r) -> Int -> [a] -> r+splitAtExact_ :: Partial => (String -> r) -> ([a] -> r) -> (a -> r -> r) -> Int -> [a] -> r splitAtExact_ err nil cons o xs     | o < 0 = err $ "index must not be negative, index=" ++ show o     | otherwise = f o xs@@ -57,7 +60,7 @@   {-# INLINE zipWithExact_ #-}-zipWithExact_ :: (String -> r) -> r -> (a -> b -> r -> r) -> [a] -> [b] -> r+zipWithExact_ :: Partial => (String -> r) -> r -> (a -> b -> r -> r) -> [a] -> [b] -> r zipWithExact_ err nil cons = f     where         f (x:xs) (y:ys) = cons x y $ f xs ys@@ -67,7 +70,7 @@   {-# INLINE zipWith3Exact_ #-}-zipWith3Exact_ :: (String -> r) -> r -> (a -> b -> c -> r -> r) -> [a] -> [b] -> [c] -> r+zipWith3Exact_ :: Partial => (String -> r) -> r -> (a -> b -> c -> r -> r) -> [a] -> [b] -> [c] -> r zipWith3Exact_ err nil cons = f     where         f (x:xs) (y:ys) (z:zs) = cons x y z $ f xs ys zs@@ -84,25 +87,25 @@ -- > takeExact n xs = -- >   | n >= 0 && n <= length xs = take n xs -- >   | otherwise                = error "some message"-takeExact :: Int -> [a] -> [a]+takeExact :: Partial => Int -> [a] -> [a] takeExact = splitAtExact_ (addNote "" "takeExact") (const []) (:)  -- | -- > dropExact n xs = -- >   | n >= 0 && n <= length xs = drop n xs -- >   | otherwise                = error "some message"-dropExact :: Int -> [a] -> [a]+dropExact :: Partial => Int -> [a] -> [a] dropExact = splitAtExact_ (addNote "" "dropExact") id (flip const)  -- | -- > splitAtExact n xs = -- >   | n >= 0 && n <= length xs = splitAt n xs -- >   | otherwise                = error "some message"-splitAtExact :: Int -> [a] -> ([a], [a])+splitAtExact :: Partial => Int -> [a] -> ([a], [a]) splitAtExact = splitAtExact_ (addNote "" "splitAtExact")     (\x -> ([], x)) (\a b -> first (a:) b) -takeExactNote :: String -> Int -> [a] -> [a]+takeExactNote :: Partial => String -> Int -> [a] -> [a] takeExactNote note = splitAtExact_ (addNote note "takeExactNote") (const []) (:)  takeExactMay :: Int -> [a] -> Maybe [a]@@ -111,7 +114,7 @@ takeExactDef :: [a] -> Int -> [a] -> [a] takeExactDef def = fromMaybe def .^ takeExactMay -dropExactNote :: String -> Int -> [a] -> [a]+dropExactNote :: Partial => String -> Int -> [a] -> [a] dropExactNote note = splitAtExact_ (addNote note "dropExactNote") id (flip const)  dropExactMay :: Int -> [a] -> Maybe [a]@@ -120,7 +123,7 @@ dropExactDef :: [a] -> Int -> [a] -> [a] dropExactDef def = fromMaybe def .^ dropExactMay -splitAtExactNote :: String -> Int -> [a] -> ([a], [a])+splitAtExactNote :: Partial => String -> Int -> [a] -> ([a], [a]) splitAtExactNote note = splitAtExact_ (addNote note "splitAtExactNote")     (\x -> ([], x)) (\a b -> first (a:) b) @@ -138,18 +141,18 @@ -- > zipExact xs ys = -- >   | length xs == length ys = zip xs ys -- >   | otherwise              = error "some message"-zipExact :: [a] -> [b] -> [(a,b)]+zipExact :: Partial => [a] -> [b] -> [(a,b)] zipExact = zipWithExact_ (addNote "" "zipExact") []  (\a b xs -> (a,b) : xs)  -- | -- > zipWithExact f xs ys = -- >   | length xs == length ys = zipWith f xs ys -- >   | otherwise              = error "some message"-zipWithExact :: (a -> b -> c) -> [a] -> [b] -> [c]+zipWithExact :: Partial => (a -> b -> c) -> [a] -> [b] -> [c] zipWithExact f = zipWithExact_ (addNote "" "zipWithExact") [] (\a b xs -> f a b : xs)  -zipExactNote :: String -> [a] -> [b] -> [(a,b)]+zipExactNote :: Partial => String -> [a] -> [b] -> [(a,b)] zipExactNote note = zipWithExact_ (addNote note "zipExactNote") []  (\a b xs -> (a,b) : xs)  zipExactMay :: [a] -> [b] -> Maybe [(a,b)]@@ -158,7 +161,7 @@ zipExactDef :: [(a,b)] -> [a] -> [b] -> [(a,b)] zipExactDef def = fromMaybe def .^ zipExactMay -zipWithExactNote :: String -> (a -> b -> c) -> [a] -> [b] -> [c]+zipWithExactNote :: Partial => String -> (a -> b -> c) -> [a] -> [b] -> [c] zipWithExactNote note f = zipWithExact_ (addNote note "zipWithExactNote") []  (\a b xs -> f a b : xs)  zipWithExactMay :: (a -> b -> c) -> [a] -> [b] -> Maybe [c]@@ -172,18 +175,18 @@ -- > zip3Exact xs ys zs = -- >   | length xs == length ys && length xs == length zs = zip3 xs ys zs -- >   | otherwise                                        = error "some message"-zip3Exact :: [a] -> [b] -> [c] -> [(a,b,c)]+zip3Exact :: Partial => [a] -> [b] -> [c] -> [(a,b,c)] zip3Exact = zipWith3Exact_ (addNote "" "zip3Exact") [] (\a b c xs -> (a, b, c) : xs)  -- | -- > zipWith3Exact f xs ys zs = -- >   | length xs == length ys && length xs == length zs = zipWith3 f xs ys zs -- >   | otherwise                                        = error "some message"-zipWith3Exact :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]+zipWith3Exact :: Partial => (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] zipWith3Exact f = zipWith3Exact_ (addNote "" "zipWith3Exact") [] (\a b c xs -> f a b c : xs)  -zip3ExactNote :: String -> [a] -> [b] -> [c]-> [(a,b,c)]+zip3ExactNote :: Partial => String -> [a] -> [b] -> [c]-> [(a,b,c)] zip3ExactNote note = zipWith3Exact_ (addNote note "zip3ExactNote") [] (\a b c xs -> (a,b,c) : xs)  zip3ExactMay :: [a] -> [b] -> [c] -> Maybe [(a,b,c)]@@ -192,7 +195,7 @@ zip3ExactDef :: [(a,b,c)] -> [a] -> [b] -> [c] -> [(a,b,c)] zip3ExactDef def = fromMaybe def .^^ zip3ExactMay -zipWith3ExactNote :: String -> (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]+zipWith3ExactNote :: Partial => String -> (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] zipWith3ExactNote note f = zipWith3Exact_ (addNote note "zipWith3ExactNote") []  (\a b c xs -> f a b c : xs)  zipWith3ExactMay :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> Maybe [d]
Safe/Foldable.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE CPP              #-}+{-# LANGUAGE ConstraintKinds  #-} {- | 'Foldable' functions, with wrappers like the "Safe" module. -}@@ -22,12 +24,13 @@ import Data.Maybe import Data.Monoid import Prelude+import Safe.Partial   --------------------------------------------------------------------- -- UTILITIES -fromNote :: String -> String -> Maybe a -> a+fromNote :: Partial => String -> String -> Maybe a -> a fromNote = fromNoteModule "Safe.Foldable"  isNull :: Foldable t => t a -> Bool@@ -44,7 +47,7 @@ foldl1May = liftMay isNull . F.foldl1 foldr1May = liftMay isNull . F.foldr1 -foldl1Note, foldr1Note :: Foldable t => String -> (a -> a -> a) -> t a -> a+foldl1Note, foldr1Note :: (Partial, Foldable t) => String -> (a -> a -> a) -> t a -> a foldl1Note note = fromNote note "foldl1Note on empty" .^ foldl1May foldr1Note note = fromNote note "foldr1Note on empty" .^ foldr1May @@ -60,7 +63,7 @@ minimumDef def = fromMaybe def . minimumMay maximumDef def = fromMaybe def . maximumMay -minimumNote, maximumNote :: (Foldable t, Ord a) => String -> t a -> a+minimumNote, maximumNote :: (Partial, Foldable t, Ord a) => String -> t a -> a minimumNote note = fromNote note "minimumNote on empty" . minimumMay maximumNote note = fromNote note "maximumNote on empty" . maximumMay @@ -72,19 +75,19 @@ minimumByDef def = fromMaybe def .^ minimumByMay maximumByDef def = fromMaybe def .^ maximumByMay -minimumByNote, maximumByNote :: Foldable t => String -> (a -> a -> Ordering) -> t a -> a+minimumByNote, maximumByNote :: (Partial, Foldable t) => String -> (a -> a -> Ordering) -> t a -> a minimumByNote note = fromNote note "minimumByNote on empty" .^ minimumByMay maximumByNote note = fromNote note "maximumByNote on empty" .^ maximumByMay  -- | -- > findJust op = fromJust . find op-findJust :: Foldable t => (a -> Bool) -> t a -> a+findJust :: (Partial, Foldable t) => (a -> Bool) -> t a -> a findJust = fromNote "" "findJust, no matching value" .^ F.find  findJustDef :: Foldable t => a -> (a -> Bool) -> t a -> a findJustDef def = fromMaybe def .^ F.find -findJustNote :: Foldable t => String -> (a -> Bool) -> t a -> a+findJustNote :: (Partial, Foldable t) => String -> (a -> Bool) -> t a -> a findJustNote note = fromNote note "findJustNote, no matching value" .^ F.find  
+ Safe/Partial.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE KindSignatures  #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE ImplicitParams  #-}+-- | ConstraintKind synonym for marking partial functions+module Safe.Partial(+  Partial+  ) where++#if __GLASGOW_HASKELL__ < 710+import GHC.Exts (Constraint)+#elif __GLASGOW_HASKELL__ < 800+import GHC.Stack (CallStack)+#else+import GHC.Stack (HasCallStack)+#endif++-- | A constraint synonym which denotes that the function is partial, and will+--   (on GHC 8.* and up) produce a stack trace on failure.+--   You may mark your own non-total functions as Partial, if necessary, and this+--   will ensure that they produce useful stack traces.+#if __GLASGOW_HASKELL__ < 710+type Partial = (() :: Constraint)+#elif __GLASGOW_HASKELL__ < 800+type Partial = (?loc :: CallStack)+#else+type Partial = HasCallStack+#endif
Safe/Util.hs view
@@ -1,27 +1,29 @@-+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds  #-} -- | Internal utilities. module Safe.Util where  import Data.Maybe+import Safe.Partial  -(.^) :: (b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c+(.^) :: Partial => (b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c (.^) f g x1 x2 = f (g x1 x2) -(.^^) :: (b -> c) -> (a1 -> a2 -> a3 -> b) -> a1 -> a2 -> a3 -> c+(.^^) :: Partial => (b -> c) -> (a1 -> a2 -> a3 -> b) -> a1 -> a2 -> a3 -> c (.^^) f g x1 x2 x3 = f (g x1 x2 x3) -(.^^^) :: (b -> c) -> (a1 -> a2 -> a3 -> a4 -> b) -> a1 -> a2 -> a3 -> a4 -> c+(.^^^) :: Partial => (b -> c) -> (a1 -> a2 -> a3 -> a4 -> b) -> a1 -> a2 -> a3 -> a4 -> c (.^^^) f g x1 x2 x3 x4 = f (g x1 x2 x3 x4)  liftMay :: (a -> Bool) -> (a -> b) -> (a -> Maybe b) liftMay test func val = if test val then Nothing else Just $ func val -fromNoteModule :: String -> String -> String -> Maybe a -> a+fromNoteModule :: Partial => String -> String -> String -> Maybe a -> a fromNoteModule modu note func = fromMaybe (error msg)     where msg = modu ++ "." ++ func ++ (if null note then "" else ", " ++ note) -fromNoteEitherModule :: String -> String -> String -> Either String a -> a+fromNoteEitherModule :: Partial => String -> String -> String -> Either String a -> a fromNoteEitherModule modu note func = either (error . msg) id     where msg ex = modu ++ "." ++ func ++ " " ++ ex ++ (if null note then "" else ", " ++ note) 
safe.cabal view
@@ -1,7 +1,7 @@ cabal-version:  >= 1.18 build-type:     Simple name:           safe-version:        0.3.11+version:        0.3.12 license:        BSD3 license-file:   LICENSE category:       Unclassified@@ -11,7 +11,7 @@ homepage:       https://github.com/ndmitchell/safe#readme synopsis:       Library of safe (exception free) functions bug-reports:    https://github.com/ndmitchell/safe/issues-tested-with:    GHC==8.0.1, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2+tested-with:    GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2 description:     A library wrapping @Prelude@/@Data.List@ functions that can throw exceptions, such as @head@ and @!!@.     Each unsafe function has up to four variants, e.g. with @tail@:@@ -50,6 +50,7 @@         Safe         Safe.Exact         Safe.Foldable+        Safe.Partial      other-modules:         Safe.Util