This file acts both as a presentation of the Floskell formatting styles, as well as a set of regression tests.
You can see how a particular style will format Haskell source by reading the matching Markdown file in the styles/ directory.
For regression testing, the canonical source, TEST.md in the root directory, is parsed and each Haskell code block formatted according to all predefined styles. The formatted output is then compared with the corresponding, already formatted code block in the <style>.md file in the styles/ subdirectory.
The regression test will also verify that repeated invocations of the pretty printer will not modify an already formatted piece of code.
The following code block acts as a quick presentation for the different formatting styles, by presenting a mixture of common Haskell constructs.
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{- |
Module: Style.Haskell.Example
Haskell Code Style Example.
-}
module Style.Haskell.Example (
-- * Types
Enum(..)
,Either(..)
,Point(..)
-- * Functions
,hello
) where
-- Module imports
import qualified Control.Monad.Trans.State (State,evalState,execState,get,modify,put,runState)
import qualified Data.Map as Map
import qualified Data.Text as Text
import Prelude hiding (map)
-- Data declarations
data Enum=CaseA|CaseB|CaseC deriving(Eq,Enum,Show)
data Either a b=Left a|Right b deriving(Eq,Show)
data Point=Point{pointX::Float,pointY::Float,pointLabel::String}deriving(Eq,Show)
-- Type classes
class Functor f=>Applicative a where
pure::b->a b
ap::a (b->c)->a b->a c
class Fundep a b|a->b where
convert::a->b
instance Functor f=>Functor(Wrap f)where
fmap f (Wrap x)=Wrap $ fmap f x
-- Values
origin::Point
origin=Point{pointX=0,pointY=0,pointLabel="Origin"}
lorem::[String]
lorem=["Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"Curabitur nec ante nec mauris ornare suscipit.",
"In ac vulputate libero.",
"Duis eget magna non purus imperdiet molestie nec quis mauris.",
"Praesent blandit quam vel arcu pellentesque, id aliquet turpis faucibus."]
-- Functions
facs::[Int]
facs=[1,1]++zipWith(+)(tailfacs)
hello::MonadIO m=>m ()
hello=do name<-liftIO getLine
liftIO . putStrLn $ greetings name
where
greetings n="Hello "++n++"!"
letExpr::Point->String
letExp x=let y=1
z=2
in if x>0 then y else z
ifExpr::Bool->Bool
ifExpr b=if b == True then False else True
caseExpr::[a]->Maybe a
caseExpr xs=case xs of
[] -> Nothing
(x:_) -> Just x
guarded::Int->Int
guarded x|x == 0=1
|x == 1=1
|otherwise=guarded (x - 2) + guarded (x - 1)
someLongFunctionNameWithALotOfParameters::
(MonadIO m,MonadRandom m)=>String->(String->String)->m ()
someLongFunctionNameWithALotOfParameters=undefined
Without exports
module Main where
With exports
module Main (foo,bar,baz,main) where
With exports and comments
module Main (
-- * Main Program
main
-- * Functions
, foo -- foo function
, bar -- bar function
, baz -- baz function
) where
With deprecation
module Main {-# DEPRECATED "no longer supported" #-} where
With warnings
module Main {-# WARNING "do not use" #-} where
import Prelude
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.ByteString (ByteString,pack,unpack)
import qualified Data.ByteString as BS (pack, unpack)
import Control.Monad hiding (forM)
import {-# SOURCE #-} safe qualified "foo" Foo as F
type Name = String
type Pair a = (a, a)
type Fun a b = a -> b
data Void
data Unit = Unit
data Maybe a = Nothing | Just a
data Num a => SomeNum = SomeNum a
newtype RWS r w s = RWS (ReaderT r (WriterT w (StateT s Identity)))
deriving (Functor, Applicative, Monad)
data Enum =
One -- Foo
| Two -- Bar
| Three -- Baz
data Foo deriving ()
data Foo deriving Show
data Foo deriving (Show)
data Foo deriving (Eq, Ord)
data Expr :: * -> * where
Const :: Int -> Expr Int
Plus :: Expr Int -> Expr Int -> Expr Int
Eq :: Expr Int -> Expr Int -> Expr Bool
deriving (Show)
data Term a where
Lit :: { val :: Int } -> Term Int
Succ :: { num :: Term Int } -> Term Int
Pred :: { num :: Term Int } -> Term Int
IsZero :: { arg :: Term Int } -> Term Bool
Pair :: { arg1 :: Term a, arg2 :: Term b } -> Term (a,b)
If :: { cnd :: Term Bool, tru :: Term a, fls :: Term a } -> Term a
type family Mutable v
type family Mutable v = (r :: *)
type family Mutable v = r | r -> v
type instance Mutable Int = MIntVector
type family Store a where
Store Bool = [Int]
Store a = [a]
type family Store a = (r :: *) where
Store a = [a]
type family Store a = r | r -> a where
Store a = [a]
data family List a
data instance List () = NilList Int
data instance List Char = CharNil | CharCons Char (List Char)
deriving (Eq, Ord, Show)
data instance List Int :: * where
IntNil :: List Int
IntCons :: Int -> List Int
deriving (Eq, Ord, Show)
data instance List Int :: * where
IntNil :: List Int
IntCons :: { val :: Int } -> List Int
deriving (Eq, Ord, Show)
newtype Penalty = Penalty Int
deriving (Eq, Ord)
deriving stock (Read, Show)
deriving newtype (Num)
deriving anyclass (FromJSON, ToJSON)
deriving (Semigroup, Monoid) via M.Sum Int
class Monoid a where
mempty :: a
mappend :: a -> a -> a
class Applicative m => Monad m where
fail :: m a
return :: a -> m a
(>>=) :: a -> (a -> m b) -> m b
class Monad m => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
state :: (s -> (a, s)) -> m a
class ToJSON a where
toJSON :: a -> Value
default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
toJSON = genericToJSON defaultOptions
instance ToJSON ()
instance Bounded Bool where
minBound = False
maxBound = True
instance Semigroup a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
instance Data () where
type Base = ()
newtype Wrapped = Wrapped { unWrap :: () }
data Expr :: * -> * where
Const :: Int -> Expr Int
Plus :: Expr Int -> Expr Int -> Expr Int
Eq :: Expr Int -> Expr Int -> Expr Bool
deriving instance Eq a => Eq (Sum a)
deriving instance {-# OVERLAP #-} Eq a => Eq (Sum a)
deriving stock instance {-# OVERLAPS #-} Eq a => Eq (Sum a)
deriving anyclass instance {-# OVERLAPPING #-} Eq a => Eq (Sum a)
deriving newtype instance {-# OVERLAPPABLE #-} Eq a => Eq (Sum a)
infix 4 ==, /=, <, <=, >, >=
infixr 0 $
infixl !!
default ()
default (Integer, Double)
$foo
$(bar baz)
id :: a -> a
sort :: Ord a => [a] -> [a]
long :: (IsString a, Monad m) => ByteString -> ByteString -> ByteString -> ByteString -> ByteString -> a -> m ()
mktime :: Int -- hours
-> Int -- minutes
-> Int -- seconds
-> Time
transform :: forall a. St -> State St a -> EitherT ServantErr IO a
{-# LANGUAGE PatternSynonyms #-}
pattern MyJust :: a -> Maybe a
pattern MyJust a = Just a
pattern MyPoint :: Int -> Int -> (Int, Int)
pattern MyPoint{x, y} = (x,y)
pattern ErrorCall :: String -> ErrorCall
pattern ErrorCall s <- ErrorCallWithLocation s _
where
ErrorCall s = ErrorCallWithLocation s ""
pattern IsTrue :: Show a => a
pattern IsTrue <- ((== "True") . show -> True)
pattern ExNumPat :: () => Show b => b -> T
pattern ExNumPat x = MkT x
pattern Foo, Bar :: Show a => a
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnboxedSums #-}
{-# LANGUAGE RecordWildCards #-}
pi = 3.14
id x = x
not False = True
not _ = False
head (x : _) = x
maybe x _ Nothing = x
maybe _ f (Some x) = f x
fst (x, _) = x
fst' (# x, _ #) = x
fstPrism (# x | | #) = Just x
fstPrism (# | _ | #) = Nothing
fstPrism (# | | _ #) = Nothing
empty [] = True
empty _ = False
unSum (Sum { getSum = s }) = s
mag2 Point{x, y} = sqr x + sqr y
mag2 Point{..} = sqr x + sqr y
strict !x = x
irrefutable ~x = x
(//) a b = undefined
a // b = undefined
main = do
greet "World"
where
greet who = putStrLn $ "Hello, " ++ who ++ "!"
{-# LANGUAGE ForeignFunctionInterface #-}
foreign import ccall sin :: Double -> Double
foreign import ccall "sin" sin :: Double -> Double
foreign import ccall "sin" sin :: Double -> Double
foreign import ccall unsafe exit :: Double -> Double
foreign export ccall callback :: Int -> Int
{-# RULES #-}
{-# RULES "map/map" forall f g xs. map f (map g xs) = map (f.g) xs #-}
{-# RULES "map/append" [2] forall f xs ys. map f (xs ++ ys) = map f xs ++ map f ys #-}
{-# DEPRECATED #-}
{-# DEPRECATED foo "use bar instead" #-}
{-# DEPRECATED foo, bar, baz "no longer supported" #-}
{-# WARNING #-}
{-# WARNING foo "use bar instead" #-}
{-# WARNING foo, bar, baz "no longer supported" #-}
{-# INLINE foo #-}
{-# INLINE [3] foo #-}
{-# INLINE [~3] foo #-}
{-# NOINLINE foo #-}
{-# INLINE CONLIKE foo #-}
{-# INLINE CONLIKE [3] foo #-}
{-# SPECIALISE foo :: Int -> Int #-}
{-# SPECIALISE [3] foo :: Int -> Int, Float -> Float #-}
{-# SPECIALISE INLINE foo :: Int -> Int #-}
{-# SPECIALISE NOINLINE foo :: Int -> Int #-}
{-# SPECIALISE instance Foo Int #-}
{-# SPECIALISE instance forall a. (Ord a) => Foo a #-}
{-# ANN foo (Just "Foo") #-}
{-# ANN type Foo (Just "Foo") #-}
{-# ANN module (Just "Foo") #-}
{-# MINIMAL foo | bar, (baz | quux) #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnboxedSums #-}
foo = foo
foo = Nothing
foo = 123
foo = 'x'
foo = ""
foo = "Lorem Ipsum Dolor Amet Sit"
foo = ()
foo = (1, 2)
foo = (1 -- the one
, 2)
foo = (1,)
foo = (,2)
foo = (,2,)
foo = (# #)
foo = (# 1, 2 #)
foo = (# 1 -- the one
, 2 #)
foo = (# 1 #)
foo = (# | 1 | | #)
foo = (# | 1 -- the one
| | #)
foo = []
foo = [1]
foo = [1,2]
foo = [1 -- the one
, 2]
foo = 1 :: Int
foo = foldl fn init list
foo = foldl fn -- reducer
init -- initial value
list
foo = 1 + 2
foo = fn `map` list
foo = -3
foo = (+ arg)
foo = (`op` arg)
foo = (arg +)
foo = (arg `op`)
foo = [1..]
foo = [1..10]
foo = [1, 2..]
foo = [1, 2..10]
foo = [:1..10:]
foo = [:1, 2..10:]
{-# LANGUAGE TransformListComp #-}
foo = [ (x, y) | x <- xs, y <- ys ]
foo = [ (x, y) -- cartesian product
| x <- xs -- first list
, y <- ys -- second list
]
foo = [ (x,y) | x <- xs | y <- ys ]
foo = [ (x,y) -- zip
| x <- xs -- first list
| y <- ys -- second list
]
foo = [: (x,y) | x <- xs | y <- ys :]
foo = [: (x,y) -- zip
| x <- xs -- first list
| y <- ys -- second list
:]
foo = [ (x, y)
| x <- xs
, y <- ys
, then reverse
, then sortWith by (x+y)
, then group using permutations
, then group by (x+y) using groupWith
]
{-# LANGUAGE RecordWildCards #-}
foo = Point { x = 1, y = 2 }
foo = Point { x = 1 -- the one
, y
, ..
}
foo = bar { x = 1 }
foo = bar { x = 1 -- the one
, y
, ..
}
{-# LANGUAGE MultiWayIf #-}
foo = let x = x in x
foo = let x = x -- bottom
in
-- bottom
x
foo = if null xs then None else Some $ head xs
foo = if null xs -- condition
then None -- it's empty
else Some $ head xs -- it's not
foo = if | null xs -> None
| otherwise -> Some $ head xs
foo = if | null xs ->
-- it's empty
None
| otherwise ->
-- it's not
Some $ head x
foo = case x of
True -> False
False -> True
foo = case xs of
[] ->
-- it's empty
None
x : _ ->
-- it's not
Some x
foo = case xs of
_ | null xs -> None
_ -> Some $ head x
{-# LANGUAGE RecursiveDo #-}
foo = do { return () }
foo = do
return ()
foo = do
this <- that
let this' = tail this
if this -- condition
then that
else those
foo = mdo
return ()
{-# LANGUAGE LambdaCase #-}
foo = \x -> x
foo = \ ~x -> x
foo = \ !x -> x
foo d = \case
Nothing -> d
Some x -> x
{-# LANGUAGE TemplateHaskell #-}
mkDecl :: Q Decl
mkDecl = [d|id x = x|]
mkType :: Q Type
mkType = [t|(a, b) -> a|]
mkPat :: Q Pat
mkPat = [p|(a, b)|]
mkExp :: Q Exp
mkExp = [e|a|]
fst :: $(mkType)
fst $(mkPat) = $(mkExp)
html = [html|<p>Lorem Ipsum Dolor Amet Sit</p>|]
foo = mkSomething 'id 'Nothing ''Maybe
Before comments and onside indent do not mix well.
foo = do
-- comment
some expression
Long types allow linebreaks.
newtype MyMonadT a b m =
MyMonad { runMyMonad :: StateT ([(a, a -> b)]) (ReaderT a (ExceptT [IM.IntMap b]) (WriterT [IS.IntSet x] m))
}
Promoted types.
type Foo = Bar 1 "foo" '() '(A, B) '[X, Y]
Long function pattern matches allow linebreaks.
doThing
(Constructor field1 field2 field3)
(Constructor field1 field2 field3)
(Constructor field1 field2 field3)
= undefined
Indent within onside started on non-empty line should still not stack.
foo = if cond
then do
this
else do
that
Before comments at the start of onside do not trigger onside.
foo = do
-- comment
some expression
Matche arms have individual onside.
foo True = some -- comment
expression
foo False = some -- comment
other expression
Where binds are considered outside of onside.
foo = some -- comment
expression
where
expression = other
Align overrides onside.
foo = some expr [ 1 -- comment
, 2
]
If-then-else must always indent in do blocks.
foo = do
if condition -- comment
then this
else that
Lists must not suppress onside.
foo = case x of
[ y -- comment
, z] -> bar
foo = do
[ x -- comment
, y ]
Don't be too eager in assigning comments to the following AST node.
data Foo = Foo
{ fooBar :: Text
-- ^A comment, long enough to end up on its own line, or at least I hope so.
} deriving (Eq)
Keep comments together and aligned.
-- block
-- one
data Foo = Foo -- some
-- comments
| Quux -- more
-- comments
-- block
-- two
... even when haskell-src-exts has weird column span info.
module Main where
-- comment
instance Foo Bar where
foo = undefined
bar = undefined
Only comments.
-- some comment
Make sure no comments are dropped from operators or argument.
foo = some -- comment 1
-- comment 2
%~ -- comment 3
argument -- comment 4
Comments after where
stay there.
consM :: Monad m => m a -> Stream m a -> Stream m a
consM m (Stream step state) = Stream step1 Nothing
where
{-# INLINE_LATE step1 #-}
step1 _ _ = undefined
Comments between declarations do not cause empty lines.
f :: ()
--
f = ()
Preprocessor directives are accepted and retained.
#include <file.h>
instance Monoid Penalty where
mempty = 0
#if !(MIN_VERSION_base(4,11,0))
mappend = (<>)
#endif
#define FOO 1
#undef FOO
#warning "WARNING"
#error "ERROR"
Multiline directives
#define FOO \
bar
#if FOO \
&& BAR
foo = bar
#endif
Support for Hsc2hs #enum
#enum CInt, \
, fs = CTL_FS \
, hw = CTL_HW \
, kern = CTL_KERN \
, net = CTL_NET \
, vfs = CTL_VFS \
, mem = CTL_VM
Ignore lines nested in floskell-disable
and floskell-enable
-- floskell-disable
This is ignored.
-- floskell-enable
{- floskell-disable -}
And so is this.
{- floskell-enable -}
Ignore shebang lines
#! /usr/bin/env nix-shell
#! nix-shell -p ghcid
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [ shower ])"
#! nix-shell -i "ghcid -c 'ghci -Wall' -T':!pkill --full ghc\\ .\\*./Main.hs' -T main"
module Main where
import Shower
main :: IO ()
main = printer "Hello"
Preserving indentation and line prefixes so that Floskell can be run on individual declarations and quoted haskell code.
data Enum =
One -- Foo
| Two -- Bar
| Three -- Baz
>
> data Enum =
> One -- Foo
> | Two -- Bar
> | Three -- Baz
>
Long module exports don't force overlong line.
module SimpleFunctions ( identity, compose, append, firstElement, secondElem ) where