hydrogen-prelude 0.8 → 0.20
raw patch · 7 files changed
Files
- CHANGELOG.md +78/−2
- README.md +309/−0
- Setup.hs +2/−0
- hydrogen-prelude.cabal +37/−28
- src/Hydrogen/Prelude.hs +373/−36
- src/Hydrogen/Prelude/IO.hs +0/−7
- src/Hydrogen/Prelude/System.hs +80/−0
CHANGELOG.md view
@@ -1,6 +1,5 @@ ## v0.1 + initial release-+ ***deprecated*** ## v0.1.0.1 + Patch: Modules were not properly exported in Cabal file@@ -15,7 +14,7 @@ + Added CHANGELOG.md to source package ## v0.2.1-+ Added __++ Added `__` (short hand for `undefined`) ## v0.3 + Also exports@@ -27,4 +26,81 @@ + New dependency on `hydrogen-version` + Exports `module Hydrogen.Version` as replacement for `module Data.Version` +## v0.4+*Skipped, bumped to v0.5 to be aligned with other hydrogen packages*++## v0.5++ Deriving instance `Eq` for `ZonedTime`++## v0.6++ New dependencies+ + `cereal` for serialization+ + `strict` for strict io in `Hydrogen.Prelude.IO`++ Also exports+ + `module Data.Dynamic`+ + `module Data.Typeable`++ Added `ShowBox` class (an existentially qualified container type)++## v0.7++ Newly deriving instances+ + `Serialize` for `ZonedTime`+ + `Serialize` for `Version`++## v0.8++ Added `class TMap (tmap)` and `class Has (HasKey, HasValue, (!))`++ Introduced `type List a` as alias for `[a]`++ Also exports+ + `Data.Seq`++ Added the F-Sharp inspired `|>` (which is `flip ($)`)++ Exporting only `Generic` from `GHC.Generics`++ Added `Hydrogen.Prelude.Network`++## v0.9++ Also exports+ + `module Data.Traversable`++ Added `class Container (Contained, (?))`++ `fmap` is exported as `map`++## v0.10++ Added `safeHeadAndTail`, `safeHeadAndTail2`, `firstJust`++ Little documentation improvements++ Added `findFilesRecursively` and `findFilesRecursivelyWithContext`+ in `Hydrogen.Prelude.System`++## v0.11++ Added `nicify` form `Text.Nicify`++ Upgraded dependency `hydrogen-version` from `>=1.2` to `>=1.3`++## v0.12++ Added `Default`++## v0.13++ _Skipped_++## v0.14++ Aligned with other `hydrogen-*` packages++## v0.15++ Added `Apply`++ Added `uncurry3`, `uncurry4`, `uncurry5`++## v0.16++ Changed `Apply` to use `-XMultiParamTypeClasses` and `-XTypeFamilies`+ for better type inference++ Added `$$`, `$$$`, `$$$$`, `$$$$$` (like `uncurryN`)++ Added `<$$>`, `<$$$>`, `<$$$$>`, `<$$$$$>` (like `<$>` and `uncurryN`)++## v0.17++ Removed instance `MonadPlus m => Default (m a)`.++## v0.18++ Removed dependency on `nicify`++## v0.19++ Removed `Apply` and `Applicator` again++ Lowered dependencies to work with GHC 7.6 and base 4.6 too++## v0.20++ Support for GHC 7.4, 7.6, and 7.8++ Instances for `Serializable` but also for `Binary`
README.md view
@@ -1,3 +1,312 @@+hydrogen-prelude+================ +[](https://travis-ci.org/scravy/hydrogen-prelude)+++ [`about`](#about)+ + [`scravy.de/hydrogen-prelude`](http://scravy.de/hydrogen-prelude)+ + [`hackage.haskell.org/package/hydrogen-prelude`](http://hackage.haskell.org/package/hydrogen-prelude)++ [`goodies`](#goodies)+ + [`(!)`](#--has-a--a--haskey-a--hasvalue-a)+ + [`(?)`](#--container-a--a--contained-a--bool)+ + [`tmap`](#tmap)+ + [`fmap` vs `map`](#fmap-vs-map)+ + [`__`](#__--a)+ + [`|>`](#fsharps--which-is-flip-)+ + [`safeHead`](#safehead--a--a--a)+ + [`ShowBox`](#showbox--forall-a-show-a--a--showbox)+ + [`.|`, `.&`, `.^`](#----a--bool--a--bool--a--bool)+ + [`List a`](#type-list-a--a)+ + [`Default a`](#class-default-a)+ + [`Apply a`](#class-apply-a)++ [`re-exports`](#re-exports)+ + [`Hydrogen.Prelude`](#hydrogenprelude)+ + [`Hydrogen.Prelude.IO`](#hydrogenpreludeio)+ + [`Hydrogen.Prelude.System`](#hydrogenpreludesystem)+ + [`Hydrogen.Prelude.Network`](#hydrogenpreludenetwork)++ [`FAQ`](#faq)++about+-----++A Prelude that exports much of the standard library (more then `Prelude`), without conflicts. If for example you were to import `Prelude` and `Data.List` or `Data.Foldable` you will run into ambiguous imports (regarding `foldr` for example). This Prelude aims at exporting the most general functions (in this case `foldr` from `Data.Foldable`).++It also pulls in some default packages like `cereal` for serialization and `containers` for data types like `Map` and `Set`. Every datatype exported by this Prelude comes with instances for `Serialize`.++Longs story short, instead of:++ import Prelude hiding (+ all, and, any, concat, concatMap, elem, foldl, foldl1, foldr, foldr1,+ mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum+ )+ import "base" Control.Monad hiding (+ forM, forM_, mapM, mapM_, msum, sequence, sequence_+ )+ import Data.Foldable+ import Data.Traversable+ import Data.List hiding (+ all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1,+ mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sum+ )+ +it suffices to:++ import Hydrogen.Prelude++goodies+-------++Beyond existing functions from well-known standard packages, this prelude defines a few utilities (mostly aimed at unifying functionality across different packages, like `containers` and `array`).++---++### `(!) :: Has a ⇒ a → HasKey a → HasValue a`++`(!)` is provided for several data types which associate a key and a value.++ [(1, 'a'), (3, 'v')] ! 1 → 'a'++Instances are defined for+++ `Eq k ⇒ [(k, v)]`++ `Ix i ⇒ Array i e` with `HasKey → i`++ `Ord k ⇒ Map k v` with `HasValue → v`++ `Ord k ⇒ MultiMap k v` with `HasValue → [v]`++---++### `(?) :: Container a ⇒ a → Contained a → Bool`++Check whether the element on the right is contained in the collection on the left.++ [1, 2, 4] ? 3 → False++Instances are defined for+++ `Eq a ⇒ [a]`++ `Ord a ⇒ Set a`++ `Eq a ⇒ Seq a`++ `Ord k ⇒ Map k v` with `Contained → k`++ `Ord k ⇒ MultiMap k v` with `Contained → k`++---++### `tmap`++A little bit like `fmap` but defined differently on some datatypes (applies e.g. to both components of a tuple).++ tmap succ (3, 4) → (4, 5)++Instances are defined for+++ `(a, a)`++ `(a, a, a)`++ `(a, a, a, a)`++ `[a]`++ `Seq a`++ `Map k v`++ `MultiMap k v`++---++### `fmap` vs `map`++Hydrogen Prelude exports `fmap` as `map` - the way it ought to be.++---++### `__ :: a`++A handy shortcut for `undefined`.++---++### FSharp's `|>` (which is `flip ($)`)++Use it to pipe things from one function to the other, left to right:++ head xs |> fromEnum |> show++---++### `safeHead :: a → [a] → a`++The head of the list, or the default given as first argument.++ safeHead x xs = maybe x head . listToMaybe++---++### `ShowBox :: forall a. (Show a) ⇒ a → ShowBox` ###++Wrap anything that is showable (can be used to build heterogeneous lists).++---++### `.|, .&, .^ :: (a → Bool) → (a → Bool) → (a → Bool)`++Combines predicates.++ filter (isDigit .| isLetter)++---++### `type List a = [a]`++A longhand for the type of lists, if you prefer this more wordy version.++---++### `class Default a` ###++A class that provides the `def` function for default values for types.+Instances of `MonadPlus` automatically have an instance where `def = mzero`.++Default instances for most primitive types are also provided.++---++### `class Apply a` ###++Provides the `*$*` operator which is your all-purpose application operator.+It does uncurrying (if you want to apply a tupel result of a function to+a function that is curryied, works with tupels of up to 5 components) and+also works with `Applicative`, i.e. it also does `fmap . uncurry`:++ data Operator = Operator Value OperatorType Value++ parseInfixOperation :: Parser (Value, OperatorType, Value)++ ... Operator *$* parseInfixOperation ...++re-exports+----------+++### Hydrogen.Prelude++The Hydrogen Prelude offers you the functions and datatypes from these modules, all with one import:+++ from [`base`](http://hackage.haskell.org/package/base)+ + `module Prelude`+ + `module Control.Applicative`+ + `module Control.Arrow`+ + `module Control.Monad`+ + `module Data.Bits`+ + `module Data.Bool`+ + `module Data.Char`+ + `module Data.Complex`+ + `module Data.Complex`+ + `module Data.Dynamic`+ + `module Data.Either`+ + `module Data.Fixed`+ + `module Data.Function`+ + `module Data.Foldable`+ + `module Data.Int`+ + `module Data.Ix`+ + `module Data.List`+ + `module Data.Maybe`+ + `module Data.Ord`+ + `module Data.Ratio`+ + `module Data.String`+ + `module Data.Time`+ + `module Data.Traversable`+ + `module Data.Tuple`+ + `module Data.Typeable`+ + `module Data.Word`+ + `module Numeric`+ + `module Text.Printf`+++ from [`array`](http://hackage.haskell.org/package/array)+ + `module Data.Array`+++ from [`cereal`](http://hackage.haskell.org/package/cereal)+ + `module Data.Serialize`+++ from [`containers`](http://hackage.haskell.org/package/containers)+ + `Data.Set`, `Data.Map`, and `Data.Seq`+++ from [`hashable`](http://hackage.haskell.org/package/hashable)+ + `module Data.Hashable`+++ form [`hydrogen-multimap`](http://hackage.haskell.org/package/hydrogen-multimap)+ + `Hydrogen.MultiMap`+++ from [`hydrogen-version`](http://hackage.haskell.org/package/hydrogen-version)+ + `module Hydrogen.Version`+++ from [`regex-tdfa`](http://hackage.haskell.org/package/regex-tdfa)+ + `module Text.Regex.TDFA`+++ from [`time`](http://hackage.haskell.org/package/time)+ + `module Data.Time`+++ from [`transformers`](http://hackage.haskell.org/package/transformers)+ + `module Data.Functor.Identity`+ + `module Data.Functor.Reverse`+++ from [`uuid`](http://hackage.haskell.org/package/uuid)+ + `Data.UUID`+ + `Data.UUID.fromString` as `uuidFromString`+ + `Data.UUID.V4.nextRandom` as `randomUUID`+++### Hydrogen.Prelude.IO+++ from [`base`](http://hackage.haskell.org/package/base)+ + `module Data.IORef`+ + `module Control.Concurret`+ + `module Control.Exception`+ + `module System.IO`+ + `module System.Timeout`+++ from [`strict`](http://hackage.haskell.org/package/strict)+ + strict IO functions `hGetContents'`, `getContents'`, `readFile'`, `interact'`+++### Hydrogen.Prelude.System+++ from [`base`](http://hackage.haskell.org/package/base)+ + `module System.CPUTime`+ + `module System.Environment`+ + `module System.Exit`+ + `module System.Info`+++ from [`directory`](http://hackage.haskell.org/package/directory)+ + `module System.Directory`+++ from [`filepath`](http://hackage.haskell.org/package/filepath)+ + `module System.FilePath`+++ from [`process`](http://hackage.haskell.org/package/process)+ + `module System.Process`+++ from [`random`](http://hackage.haskell.org/package/random)+ + `module System.Random`+++### Hydrogen.Prelude.Network+++ from [`network`](http://hackage.haskell.org/package/network)+ + `module Network`+++FAQ+===++How is some of the *magic* accomplished?+----------------------------------------++Mostly with `XTypeFamilies` and `XStandaloneDeriving`.++So this works only with GHC?+----------------------------++Yes, for now at least.++What is `hydrogen`+------------------++https://www.youtube.com/watch?v=rbBX6aEzEz8
Setup.hs view
@@ -1,2 +1,4 @@ import Distribution.Simple+ main = defaultMain+
hydrogen-prelude.cabal view
@@ -1,8 +1,8 @@ name: hydrogen-prelude-version: 0.8-homepage: https://scravy.de/hydrogen-prelude/+version: 0.20+homepage: http://scravy.de/hydrogen-prelude/ synopsis: Hydrogen Prelude-license: BSD3+license: MIT license-file: LICENSE extra-source-files: CHANGELOG.md, README.md author: Julian Fleischer@@ -12,50 +12,59 @@ cabal-version: >=1.14 source-repository head- type: git- location: https://github.com/scravy/hydrogen-prelude+ type: git+ location: https://github.com/scravy/hydrogen-prelude +flag explicitText+ library exposed-modules: Hydrogen.Prelude , Hydrogen.Prelude.IO , Hydrogen.Prelude.Network , Hydrogen.Prelude.System- build-depends: base ==4.7.*- , array ==0.5.*- , cereal ==0.4.*- , containers ==0.5.*- , directory ==1.2.*- , filepath ==1.3.*- , hashable ==1.2.*- , hydrogen-version >=1.2- , network >=2.4.2.2- , process ==1.2.*- , random ==1.0.*- , regex-base ==0.93.*- , regex-tdfa ==1.2.*- , strict ==0.3.*- , time ==1.4.*- , transformers ==0.3.*- , uuid ==1.3.*+ build-depends: base >=4.5 && <5+ , array >=0.4+ , binary >=0.5.1+ , bytestring >=0.9.2.1+ , cereal >=0.4.1.1+ , containers >=0.4.2.1+ , directory >=1.1+ , filepath >=1.3+ , hashable >=1.1+ , hydrogen-multimap >=0.3+ , hydrogen-version >=1.4+ , network >=2.3+ , process >=1.1.0.1+ , random >=1.0.1.1+ , regex-base >=0.93.1+ , regex-tdfa >=1.2+ , strict >=0.3+ , text >=0.11.2+ , time >=1.4+ , transformers >=0.3+ , uuid >=1.3+ if impl(ghc <= 7.6)+ build-depends: ghc-prim+ , text <1.2.0.4+ if impl(ghc >= 7.6)+ build-depends: binary >=0.7+ hs-source-dirs: src ghc-options: -Wall default-language: Haskell2010 default-extensions: CPP , DeriveDataTypeable+ , DeriveFoldable , DeriveFunctor , DeriveGeneric- , EmptyCase+ , DeriveTraversable , FlexibleInstances , FlexibleContexts , GADTs- , LambdaCase- , MultiWayIf- , NegativeLiterals+ , MultiParamTypeClasses , NoImplicitPrelude , PackageImports- , RankNTypes , RecordWildCards , ScopedTypeVariables , StandaloneDeriving- , TupleSections , TypeFamilies
src/Hydrogen/Prelude.hs view
@@ -24,9 +24,10 @@ , module Data.Maybe , module Data.Ord , module Data.Ratio- , module Data.Serialize , module Data.String , module Data.Time+ , module Data.Time.Calendar.OrdinalDate+ , module Data.Traversable , module Data.Tuple , module Data.Typeable , module Data.Word@@ -35,22 +36,52 @@ , module Text.Printf , (.&) , (.|)+ , (.^) , (=~) , (=~~) , (|>)- , for+ , ($$)+ , ($$$)+ , ($$$$)+ , ($$$$$)+ , (<$$>)+ , (<$$$>)+ , (<$$$$>)+ , (<$$$$$>) , uuidFromString , randomUUID , safeHead+ , safeHeadAndTail+ , safeHeadAndTail2+ , firstJust+ , uncurry3+ , uncurry4+ , uncurry5+ , map , UUID+ , ByteString+ , LazyByteString+ , Serialize+ , encode+ , encodeLazy+ , decode+ , decodeLazy+ , Binary+ , binaryEncode+ , binaryDecode+ , binaryEncodeFile+ , binaryDecodeFile , Generic , List , Map- , Set+ , MultiMap , Seq+ , Set , ShowBox , TMap (..) , Has (..)+ , Container (..)+ , Default (..) , __ ) where @@ -65,30 +96,44 @@ , foldl1 , foldr , foldr1+ , map+ , mapM , mapM_ , maximum , minimum , notElem , or , product+ , sequence , sequence_ , sum+#if MIN_VERSION_base(4,6,0)+#else+ , catch+#endif ) import "base" Control.Applicative import "base" Control.Arrow import "base" Control.Monad hiding (- forM_+ forM+ , forM_+ , mapM , mapM_ , msum+ , sequence , sequence_ ) import "array" Data.Array hiding ((!)) import qualified "array" Data.Array as Array +import "binary" Data.Binary (Binary)+import qualified "binary" Data.Binary as Binary import "base" Data.Bits hiding (bitSize) import "base" Data.Bool+import "bytestring" Data.ByteString (ByteString)+import qualified "bytestring" Data.ByteString.Lazy as LazyByteString import "base" Data.Char import "base" Data.Complex import "base" Data.Dynamic@@ -114,9 +159,12 @@ , foldl1 , foldr , foldr1+ , map+ , mapAccumL+ , mapAccumR , maximum- , minimum , maximumBy+ , minimum , minimumBy , notElem , or@@ -126,14 +174,21 @@ import "base" Data.Maybe import "base" Data.Ord import "base" Data.Ratio-import "cereal" Data.Serialize+import "cereal" Data.Serialize (Serialize)+import qualified "cereal" Data.Serialize as Serialize import "base" Data.String import "time" Data.Time+import "time" Data.Time.Calendar.OrdinalDate+import "base" Data.Traversable import "base" Data.Tuple import "base" Data.Typeable import "base" Data.Word +#if MIN_VERSION_base(4,6,0) import "base" GHC.Generics (Generic)+#else+import "ghc-prim" GHC.Generics (Generic)+#endif import "base" Numeric @@ -143,10 +198,11 @@ import "containers" Data.Map (Map) import qualified "containers" Data.Map as Map import "containers" Data.Set (Set)--- import qualified "containers" Data.Set as Set+import qualified "containers" Data.Set as Set import "containers" Data.Sequence (Seq)--- import qualified "containers" Data.Sequence as Seq +import "hydrogen-multimap" Hydrogen.MultiMap (MultiMap)+import qualified "hydrogen-multimap" Hydrogen.MultiMap as MultiMap import "hydrogen-version" Hydrogen.Version import "uuid" Data.UUID (UUID)@@ -154,6 +210,18 @@ import qualified "uuid" Data.UUID import qualified "uuid" Data.UUID.V4 ++-----------------------------------------------------------------------+-- Lazy type aliases+-----------------------------------------------------------------------++type LazyByteString = LazyByteString.ByteString+++-----------------------------------------------------------------------+-- Generic instances+-----------------------------------------------------------------------+ deriving instance Eq ZonedTime deriving instance Generic Day deriving instance Generic LocalTime@@ -162,59 +230,197 @@ deriving instance Generic UniversalTime deriving instance Generic ZonedTime ++-----------------------------------------------------------------------+-- Serialize & Binary instances+-----------------------------------------------------------------------+ instance Serialize Day-instance Serialize LocalTime-instance Serialize TimeOfDay+instance Binary Day+ instance Serialize TimeZone+instance Binary TimeZone+ instance Serialize UniversalTime+instance Binary UniversalTime+ instance Serialize Version+instance Binary Version++#if MIN_VERSION_base(4,7,0)+instance Serialize LocalTime+instance Binary LocalTime++instance Serialize TimeOfDay+instance Binary TimeOfDay+ instance Serialize ZonedTime+instance Binary ZonedTime instance Serialize (Fixed E12) where- put (MkFixed int) = put int+ put (MkFixed int) = Serialize.put int - get = MkFixed <$> get+ get = MkFixed <$> Serialize.get +instance Binary (Fixed E12) where+ put (MkFixed int) = Binary.put int++ get = MkFixed <$> Binary.get+#endif+ instance Serialize UUID where - put uuid = do- let p = putWord32be- (w1, w2, w3, w4) = Data.UUID.toWords uuid- p w1 >> p w2 >> p w3 >> p w4+ put uuid = p w1 >> p w2 >> p w3 >> p w4+ where+ p = Serialize.putWord32be+ (w1, w2, w3, w4) = Data.UUID.toWords uuid - get = let g = getWord32be in liftM4 Data.UUID.fromWords g g g g- + get = liftM4 Data.UUID.fromWords g g g g+ where+ g = Serialize.getWord32be++-- instance Binary UUID is already provided by UUID package+++-----------------------------------------------------------------------+-- Data.Serialize+-----------------------------------------------------------------------++encode :: Serialize a => a -> ByteString+-- ^ Encode a value using binary serialization to a strict ByteString.+encode = Serialize.encode++encodeLazy :: Serialize a => a -> LazyByteString+-- ^ Encode a value using binary serialization to a lazy ByteString.+encodeLazy = Serialize.encodeLazy++decode :: Serialize a => ByteString -> Either String a+-- ^ Decode a value from a strict ByteString, reconstructing the+-- original structure.+decode = Serialize.decode++decodeLazy :: Serialize a => LazyByteString -> Either String a+-- ^ Decode a value from a lazy ByteString, reconstructing the+-- original structure.+decodeLazy = Serialize.decodeLazy+++-----------------------------------------------------------------------+-- Data.Binary+-----------------------------------------------------------------------++binaryEncode :: Binary a => a -> LazyByteString+-- ^ Encode a value using binary serialisation to a lazy ByteString.+binaryEncode = Binary.encode++binaryDecode :: Binary a => LazyByteString -> a+-- ^ Decode a value from a lazy ByteString, reconstructing the+-- original structure.+binaryDecode = Binary.decode++binaryEncodeFile :: Binary a => FilePath -> a -> IO ()+-- ^ Lazily serialise a value to a file.+binaryEncodeFile = Binary.encodeFile++binaryDecodeFile :: Binary a => FilePath -> IO a+-- ^ Decode a value from a file. In case of errors, error will be+-- called with the error message.+binaryDecodeFile = Binary.decodeFile+++-----------------------------------------------------------------------+-- ShowBox+-----------------------------------------------------------------------+ data ShowBox where ShowBox :: forall a. (Show a) => a -> ShowBox instance Show ShowBox where show (ShowBox a) = show a -(.|), (.&) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)++-----------------------------------------------------------------------+-- Composition of boolean functions+-----------------------------------------------------------------------++(.|), (.&), (.^) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool) f .| g = \x -> f x || g x f .& g = \x -> f x && g x+f .^ g = \x -> f x /= g x ++-----------------------------------------------------------------------+-- Nice shorthands & operators+-----------------------------------------------------------------------+ (|>) :: a -> (a -> b) -> b+-- ^ @flip ('$')@ (|>) = flip ($) -for :: Functor f => f a -> (a -> b) -> f b-for = flip fmap- __ :: a+-- ^ A shorthand for 'undefined'. __ = error "Hydrogen.Prelude.undefined" ++-----------------------------------------------------------------------+-- UUID functions+-----------------------------------------------------------------------+ uuidFromString :: String -> Maybe UUID uuidFromString = Data.UUID.fromString randomUUID :: IO UUID+-- ^ Produces a random V4 UUID (alias for 'Data.UUID.V4.nextRandom'). randomUUID = Data.UUID.V4.nextRandom -safeHead :: a -> [a] -> a-safeHead d = \case++-----------------------------------------------------------------------+-- Safe functions+-----------------------------------------------------------------------++safeHead+ :: a -- ^ The default value for the case of the empty list.+ -> [a] -- ^ The list.+ -> a+-- ^ Returns the head of the list or the default value.+safeHead default_ = \z -> case z of x : _ -> x- _ -> d+ _ -> default_ +safeHeadAndTail :: a -> [a] -> (a, [a])+safeHeadAndTail default_ = \z -> case z of+ x : xs -> (x, xs)+ _ -> (default_, []) +safeHeadAndTail2 :: a -> a -> [a] -> (a, a, [a])+safeHeadAndTail2 d1 d2 = \z -> case z of+ x : y : xs -> (x, y, xs)+ x : xs -> (x, d2, xs)+ xs -> (d1, d2, xs)+++-----------------------------------------------------------------------+-- firstJust+-----------------------------------------------------------------------++firstJust :: [a -> Maybe b] -> a -> Maybe b+-- ^ Applies a bunch of functions on a given value,+-- returns the first result that is not Nothing+-- (or 'Nothing' if no 'Just' value was produced).+firstJust (f : fs) v = case f v of+ Nothing -> firstJust fs v+ x -> x+firstJust [] _ = Nothing+++-----------------------------------------------------------------------+-- More generic maps+-----------------------------------------------------------------------++map :: Functor f => (a -> b) -> f a -> f b+-- ^ map as it should be: 'fmap'.+map = fmap+ class TMap a where type Component x@@ -249,15 +455,22 @@ type Component [a] = a type Transform ((a -> b) -> [a]) = [b] - tmap = map+ tmap = fmap instance TMap (Map k v) where type Component (Map k v) = v type Transform ((v -> w) -> Map k v) = Map k w - tmap = Map.map+ tmap = fmap +instance TMap (MultiMap k v) where++ type Component (MultiMap k v) = v+ type Transform ((v -> w) -> MultiMap k v) = MultiMap k w++ tmap = fmap+ instance TMap (Seq a) where type Component (Seq a) = a@@ -266,27 +479,36 @@ tmap = fmap +-----------------------------------------------------------------------+-- Verbose `List' type+-----------------------------------------------------------------------+ type List a = [a] ++-----------------------------------------------------------------------+-- Has+-----------------------------------------------------------------------+ class Has a where - type K a- type V a+ type HasKey a+ type HasValue a - (!) :: a -> K a -> V a+ (!) :: a -> HasKey a -> HasValue a instance Ord k => Has (Map k v) where - type K (Map k v) = k- type V (Map k v) = v+ type HasKey (Map k v) = k+ type HasValue (Map k v) = v (!) = (Map.!) instance Eq k => Has [(k, v)] where - type K [(k, v)] = k- type V [(k, v)] = v+ type HasKey [(k, v)] = k+ type HasValue [(k, v)] = v list ! key = maybeKey (lookup key list) where@@ -294,9 +516,124 @@ instance Ix i => Has (Array i e) where - type K (Array i e) = i- type V (Array i e) = e+ type HasKey (Array i e) = i+ type HasValue (Array i e) = e (!) = (Array.!) +instance Ord k => Has (MultiMap k v) where++ type HasKey (MultiMap k v) = k+ type HasValue (MultiMap k v) = [v]++ (!) = flip MultiMap.lookup+++-----------------------------------------------------------------------+-- Container+-----------------------------------------------------------------------++class Container a where++ type Contained a++ (?) :: a -> Contained a -> Bool+++instance Ord a => Container (Set a) where++ type Contained (Set a) = a++ (?) = flip Set.member++instance Eq a => Container [a] where++ type Contained [a] = a++ (?) = flip elem++instance Eq a => Container (Seq a) where++ type Contained (Seq a) = a++ c ? e = any (== e) c++instance Ord k => Container (Map k v) where++ type Contained (Map k v) = k++ (?) = flip Map.member++instance Ord k => Container (MultiMap k v) where++ type Contained (MultiMap k v) = k++ (?) = flip MultiMap.member+++-----------------------------------------------------------------------+-- Default+-----------------------------------------------------------------------++class Default a where++ def :: a++instance Default Int where def = 0+instance Default Int8 where def = 0+instance Default Int16 where def = 0+instance Default Int32 where def = 0+instance Default Int64 where def = 0+instance Default Word8 where def = 0+instance Default Word16 where def = 0+instance Default Word32 where def = 0+instance Default Word64 where def = 0+instance Default Integer where def = 0++instance Default Bool where def = False+instance Default [a] where def = []+instance Default (Maybe a) where def = Nothing+++-----------------------------------------------------------------------+-- Apply / curry operators+-----------------------------------------------------------------------++infixr 0 $$+infixr 0 $$$+infixr 0 $$$$+infixr 0 $$$$$++($$) :: (a -> b -> z) -> (a, b) -> z+($$) = uncurry++($$$), uncurry3 :: (a -> b -> c -> z) -> (a, b, c) -> z+uncurry3 f (a, b, c) = f a b c+($$$) = uncurry3++($$$$), uncurry4 :: (a -> b -> c -> d -> z) -> (a, b, c, d) -> z+uncurry4 f (a, b, c, d) = f a b c d+($$$$) = uncurry4++($$$$$), uncurry5 :: (a -> b -> c -> d -> e -> z) -> (a, b, c, d, e) -> z+uncurry5 f (a, b, c, d, e) = f a b c d e+($$$$$) = uncurry5+++infixl 4 <$$>+infixl 4 <$$$>+infixl 4 <$$$$>+infixl 4 <$$$$$>++(<$$>) :: Functor f => (a -> b -> z) -> f (a, b) -> f z+(<$$>) = (<$>) . uncurry++(<$$$>) :: Functor f => (a -> b -> c -> z) -> f (a, b, c) -> f z+(<$$$>) = (<$>) . uncurry3++(<$$$$>) :: Functor f => (a -> b -> c -> d -> z) -> f (a, b, c, d) -> f z+(<$$$$>) = (<$>) . uncurry4++(<$$$$$>) :: Functor f => (a -> b -> c -> d -> e -> z) -> f (a, b, c, d, e) -> f z+(<$$$$$>) = (<$>) . uncurry5
src/Hydrogen/Prelude/IO.hs view
@@ -2,7 +2,6 @@ module Hydrogen.Prelude , module Control.Concurrent , module Control.Exception- , module Control.Monad , module Data.IORef , module System.IO , module System.Timeout@@ -18,12 +17,6 @@ import "base" Control.Concurrent import "base" Control.Exception-import "base" Control.Monad hiding (- forM_- , mapM_- , msum- , sequence_- ) import "base" System.IO import "base" System.Timeout
src/Hydrogen/Prelude/System.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+ module Hydrogen.Prelude.System ( module Hydrogen.Prelude.IO , module System.CPUTime@@ -8,6 +10,10 @@ , module System.Info , module System.Process , module System.Random+ , findFilesRecursively+ , findFilesRecursivelyWithContext+ , escapeFileName+ , unescapeFileName ) where import Hydrogen.Prelude.IO@@ -20,4 +26,78 @@ import "base" System.Info import "process" System.Process import "random" System.Random++import qualified Data.Set as Set+++findFilesRecursively :: (FilePath -> IO Bool) -> FilePath -> IO [FilePath]+findFilesRecursively f dir =+ map fst <$> findFilesRecursivelyWithContext (\c _ _ -> return c) f () dir+++findFilesRecursivelyWithContext+ :: forall c.+ (c -> FilePath -> [FilePath] -> IO c) -- ^ update function for current context+ -> (FilePath -> IO Bool) -- ^ predicate to filter files+ -> c -- ^ current context+ -> FilePath -> IO [(FilePath, c)]+findFilesRecursivelyWithContext updater predicate context dir = do++ cwd <- getCurrentDirectory+ snd <$> find Set.empty context (cwd </> dir)++ where++ find :: Set FilePath -> c -> FilePath -> IO (Set FilePath, [(FilePath, c)])+ find visited context dir = canonicalizePath dir >>= find'+ where+ find' thisDirectory+ | Set.member thisDirectory visited = return (Set.empty, [])+ | otherwise = do++ allFiles <- map (dir </>) <$> getDirectoryContents dir+ theFiles <- filterFiles allFiles+ theDirs <- filterM isDir allFiles+ context' <- updater context dir theFiles++ let visited' = Set.insert thisDirectory visited+ f (visited, files) dir = do+ (visited', files') <- find visited context' dir+ return (visited', files' : files)++ (visited'', files') <- foldM f (visited', []) theDirs++ return (visited'', concat (zip theFiles (repeat context') : files'))++ filterFiles = filterM (\x -> liftM2 (&&) (doesFileExist x) (predicate x))+ isDir x = liftM2 (&&) (doesDirectoryExist x) (return (head (takeFileName x) /= '.'))+++escapeFileName :: String -> String+escapeFileName s = case s of+ ('/' : xs)+ -> '_' : escapeFileName xs+ (x : xs)+ | isSafeChar x -> x : escapeFileName xs+ | ord x <= 255 -> '$' : printf "%02X" (ord x) ++ escapeFileName xs+ | otherwise -> "$$" ++ printf "%04X" (ord x) ++ escapeFileName xs+ [] -> []+ where+ isSafeChar x = isAscii x && isAlphaNum x || x `elem` ".-"+++unescapeFileName :: String -> Maybe String+unescapeFileName s = case s of+ ('$' : '$' : a : b : c : d : xs)+ -> (chr <$> hexnum (a : b : c : [d])) `cons` unescapeFileName xs+ ('$' : a : b : xs)+ -> (chr <$> hexnum (a : [b])) `cons` unescapeFileName xs+ ('_' : xs)+ -> pure '/' `cons` unescapeFileName xs+ (x : xs)+ -> pure x `cons` unescapeFileName xs+ [] -> return []+ where+ cons = liftA2 (:)+ hexnum = fmap fst . listToMaybe . readHex