flow 1.0.4 → 1.0.5
raw patch · 3 files changed
+234/−2 lines, 3 files
Files
- Flow.hs +226/−0
- flow.cabal +5/−1
- package.yaml +3/−1
+ Flow.hs view
@@ -0,0 +1,226 @@+{- |+ Flow provides operators for writing more understandable Haskell. It is an+ alternative to some common idioms like ('Prelude.$') for function+ application and ('Prelude..') for function composition.++ Flow is designed to be imported unqualified. It does not export anything+ that conflicts with the base package.++ >>> import Flow++ == Rationale++ I think that Haskell can be hard to read. It has two operators for applying+ functions. Both are not really necessary and only serve to reduce+ parentheses. But they make code hard to read. People who do not already+ know Haskell have no chance of guessing what @foo $ bar@ or @baz & qux@+ mean.++ Those that do know Haskell are forced to read lines forwards and backwards+ at the same time, thanks to function composition. Even something simple,+ like finding the minimum element, bounces around: @f = head . sort@.++ I think we can do better. By using directional operators, we can allow+ readers to move their eye in only one direction, be that left-to-right or+ right-to-left. And by using idioms common in other programming languages,+ we can allow people who aren't familiar with Haskell to guess at the+ meaning.++ So instead of ('Prelude.$'), I propose ('<|'). It is a pipe, which anyone+ who has touched a Unix system should be familiar with. And it points in the+ direction it sends arguments along. Similarly, replace ('Prelude.&') with+ ('|>'). And for composition, ('<.') replaces ('Prelude..'). I would have+ preferred @<<@, but its counterpart @>>@ is taken by Haskell's syntax.+ So-called "backwards" composition is normally expressed with+ ('Control.Category.>>>'), which Flow provides as ('.>').+-}+module Flow (+ -- * Function application+ (|>), (<|), apply,+ -- * Function composition+ (.>), (<.), compose,+ -- * Strict function application+ (!>), (<!), apply',+) where++import Prelude (seq)++{- $setup+ >>> import Prelude+ >>> let f = (+ 3)+ >>> let g = (* 3)+ >>> let h = (^ 3)+-}++{- |+ prop> (x |> f) == f x++ prop> (x |> f |> g) == g (f x)++ Left-associative 'apply' operator. Read as "apply forward" or "pipe into".+ Use this to create long chains of computation that suggest which direction+ things move in.++ >>> 3 |> succ |> recip |> negate+ -0.25++ Or use it anywhere you would use ('Prelude.&').+-}+infixl 0 |>+(|>) :: a -> (a -> b) -> b+x |> f = apply x f++{- |+ prop> (f <| x) == f x++ prop> (g <| f <| x) == g (f x)++ Right-associative 'apply' operator. Read as "apply backward" or "pipe+ from". Use this to create long chains of computation that suggest which+ direction things move in. You may prefer this operator over ('|>') for+ 'Prelude.IO' actions since it puts the last function first.++ >>> print <| negate <| recip <| succ <| 3+ -0.25++ Or use it anywhere you would use ('Prelude.$').+-}+infixr 0 <|+(<|) :: (a -> b) -> a -> b+f <| x = apply x f++{- |+ prop> apply x f == f x++ Function application. This function usually isn't necessary, but it can be+ more readable than some alternatives when used with higher-order functions+ like 'Prelude.map'.++ >>> map (apply 2) [succ, recip, negate]+ [3.0,0.5,-2.0]+-}+apply :: a -> (a -> b) -> b+apply x f = f x++{- |+ prop> (f .> g) x == g (f x)++ prop> (f .> g .> h) x == h (g (f x))++ Left-associative 'compose' operator. Read as "compose forward" or "and+ then". Use this to create long chains of computation that suggest which+ direction things move in.++ >>> let f = succ .> recip .> negate+ >>> f 3+ -0.25++ Or use it anywhere you would use ('Control.Category.>>>').+-}+infixl 9 .>+(.>) :: (a -> b) -> (b -> c) -> (a -> c)+f .> g = compose f g++{- |+ prop> (g <. f) x == g (f x)++ prop> (h <. g <. f) x == h (g (f x))++ Right-associative 'compose' operator. Read as "compose backward" or "but+ first". Use this to create long chains of computation that suggest which+ direction things move in. You may prefer this operator over ('.>') for+ 'Prelude.IO' actions since it puts the last function first.++ >>> let f = print <. negate <. recip <. succ+ >>> f 3+ -0.25++ Or use it anywhere you would use ('Prelude..').+-}+infixr 9 <.+(<.) :: (b -> c) -> (a -> b) -> (a -> c)+g <. f = compose f g++{- |+ prop> compose f g x == g (f x)++ Function composition. This function usually isn't necessary, but it can be+ more readable than some alternatives when used with higher-order functions+ like 'Prelude.map'.++ >>> let fs = map (compose succ) [recip, negate]+ >>> map (apply 3) fs+ [0.25,-4.0]+-}+compose :: (a -> b) -> (b -> c) -> (a -> c)+compose f g = \ x -> g (f x)++{- |+ prop> (x !> f) == seq x (f x)++ prop> (x !> f !> g) == let y = seq x (f x) in seq y (g y)++ Left-associative 'apply'' operator. Read as "strict apply forward" or+ "strict pipe info". Use this to create long chains of computation that+ suggest which direction things move in.++ >>> 3 !> succ !> recip !> negate+ -0.25++ The difference between this and ('|>') is that this evaluates its argument+ before passing it to the function.++ >>> undefined |> const True+ True+ >>> undefined !> const True+ *** Exception: Prelude.undefined+-}+infixl 0 !>+(!>) :: a -> (a -> b) -> b+x !> f = apply' x f++{- |+ prop> (f <! x) == seq x (f x)++ prop> (g <! f <! x) == let y = seq x (f x) in seq y (g y)++ Right-associative 'apply'' operator. Read as "strict apply backward" or+ "strict pipe from". Use this to create long chains of computation that+ suggest which direction things move in. You may prefer this operator over+ ('!>') for 'Prelude.IO' actions since it puts the last function first.++ >>> print <! negate <! recip <! succ <! 3+ -0.25++ The difference between this and ('<|') is that this evaluates its argument+ before passing it to the function.++ >>> const True <| undefined+ True+ >>> const True <! undefined+ *** Exception: Prelude.undefined+-}+infixr 0 <!+(<!) :: (a -> b) -> a -> b+f <! x = apply' x f++{- |+ prop> apply' x f == seq x (f x)++ Strict function application. This function usually isn't necessary, but it+ can be more readable than some alternatives when used with higher-order+ functions like 'Prelude.map'.++ >>> map (apply' 2) [succ, recip, negate]+ [3.0,0.5,-2.0]++ The different between this and 'apply' is that this evaluates its argument+ before passing it to the function.++ >>> apply undefined (const True)+ True+ >>> apply' undefined (const True)+ *** Exception: Prelude.undefined+-}+apply' :: a -> (a -> b) -> b+apply' x f = seq x (apply x f)
flow.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: flow-version: 1.0.4+version: 1.0.5 synopsis: Write more understandable Haskell. description: Flow provides operators for writing more understandable Haskell. category: Combinators, Functions, Utility@@ -29,6 +29,10 @@ ghc-options: -Wall build-depends: base <5+ exposed-modules:+ Flow+ other-modules:+ Paths_flow default-language: Haskell2010 test-suite flow-test-suite
package.yaml view
@@ -12,6 +12,8 @@ library: dependencies: - base <5+ exposed-modules:+ - Flow license: MIT maintainer: Taylor Fausak name: flow@@ -25,4 +27,4 @@ - QuickCheck ==2.* - template-haskell ==2.* main: FlowTest.hs-version: '1.0.4'+version: '1.0.5'