$ |
Use in place of parenthesis for function calls/applicatives/etc. a (b c) = a $ b c |
. |
Function composition, useful in the "point free style" with no arguments f = g . h |
liftIO |
When you have something that returns an IO monad f, and you want to put it where some other (but containing IO) monad should be, then you can do liftIO f. It converts an IO monad into the required monad if compatible. |
<$> |
Shorthand for fmap; maps a function taking plain old types onto data wrapped in a functor, applicative, or monad, returning wrapped contents |
<*> |
Similar to <$>, but also the function is wrapped in an applicative, just like the arguments and result |
sequenceA |
flips around applicatives e.g. turns a list of Maybe into Just a list or Nothing |
traverse |
Applicative f => (a -> f b) -> t a -> f (t b) maps a function reurning an applicative and then flips the applicative outside of the e.g. list |
[1..5] |
= [1,2,3,4,5] |
[1,3..9] |
= [1,3,6,9] note lists can only have even spacing between values |
[1..] |
= [1,2,3,4,5, and so on forever] |
[ expr | var <- list, predicate ] |
List comprehension e.g. [ 2*x | x <- [0..4], x > 3] = [6,8] |
With applicatives, you can do
> (*) <$> Just 3 <*> Just 4
Just 12
and just add another <*> (Just 5) if the initial function takes 3 arguments,
and so on.
> let x = [1..3]
> let y = Just <$> x
> y
[Just 1,Just 2,Just 3]
> let z = Nothing : y
> z
[Nothing,Just 1,Just 2,Just 3]
> sequenceA y
Just [1,2,3]
> sequenceA z
Nothing
> traverse Just x
Just [1,2,3]
After Haskell made Monad a subclass of Applicative, you can always use the new (applicative or functor) name for all of these:
>> is an old name for *>.return is an old name for pure.map and liftM are old names for fmap.liftM2, liftM3, etc. are old names for liftA2, liftA3, etc.ap is an old name for the <*> operator.msum is an old name for asum.sequence and sequence_ are old names for sequenceA and sequenceA_.mapM and mapM_ are old names for traverse and traverse_.forM and forM_ are old names for for and for_.All copied from https://entropicthoughts.com/haskell-procedural-programming#things-you-never-need-to-care-about by Christoffer Stjernlöf
On the other hand, using these most general functions can make error messages
more confusing when using lists. Concatenation with ++ instead of <> (from
Semigroup) and mapping with map instead of fmap or <$> (from Functor) can
be more readable and simpler when working with a list of
monads/applicatives/etc. But beware that ++ doesn't work with Text, <> must
be used!
In the Monad do notation,
do
a <- as
bs a
Translates to as >>= bs.
x <- func: The return value of func must be the monad the do expects. The
x is unpacked out of the monad for use in later lines.func: The return value of func must be the monad the do expects. The
contents of the return value are discarded (this is just for the
side-effects)let x = func: use this if you want to capture a return value that’s not an
instance of the monad e.g. if it’s a pure function. You could instead
substitute with:x <- return $ funcreturn x wraps a value in the monad if needed. pure and return are
equivalent.If you defined a data type, then this only exports the type constructor:
module XXX
(
MyDatatype
)
This exports the data constructor (and accessor functions for a record) as well:
module XXX
(
MyDatatype (..)
)
The following GHC extensions make it so you don't have to worry about record field names colliding with other record field names or general variables and functions.
The type of record you are constructing/updating can even be inferred if the set of fields you use to construct/update the record is unique to the type.
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE OverloadedRecordDot #-}
data Foo = MkFoo { x :: Int, y :: Int }
data Bar = MkBar { x :: Int, z :: Int }
-- Constructing records:
x = MkFoo { x = 5, y = 7 }
y = MkBar { x = 0, z = 3 }
-- Updating records:
baz = x { y = 6 } -- inferred to be of type Foo
main :: IO ()
-- Accessing record fields
print baz.x -- 5
print baz.y -- 6
print y.z -- 3
-- This method of access is disabled to not have to worry about name
-- collisions:
print $ z y -- error
The following GHC extensions let you type less when pattern matching on records:
{-# LANGUAGE NamedFieldPuns #-} -- Available by default in GHC2021 and GHC2024
{-# LANGUAGE RecordWildCards #-}
data Fizz = MkFizz {alpha :: Int, beta :: Int, gamma :: Int, delta :: Int}
myfizz = MkFizz {alpha = 10, beta = 2, gamma = 3, delta = 5}
-- Field puns make it easy to extract a field by name from a record in a
-- pattern match
ten (MkFizz {alpha}) = alpha
-- Without named field puns, you have to do this:
five (MkFizz {delta = delta}) = delta
-- Record wildcards mean you match any and all used fields without having to
-- type them
twenty (MkFizz {..}) = alpha `div` beta + gamma * delta
main :: IO ()
main = do
print $ ten myfizz -- 10
print $ five myfizz -- 5
print $ twenty myfizz -- 20