Related
So I'm new to Haskell and learning it using WikiBooks. And in the higher order functions chapter, there is the following example used.
echoes = foldr (\ x xs -> (replicate x x) ++ xs) []
So I tried running it, but it gives me an error as follows :
* Ambiguous type variable `t0' arising from a use of `foldr'
prevents the constraint `(Foldable t0)' from being solved.
Relevant bindings include
echoes :: t0 Int -> [Int] (bound at HavingFun.hs:107:1)
Probable fix: use a type annotation to specify what `t0' should be.
These potential instances exist:
instance Foldable (Either a) -- Defined in `Data.Foldable'
instance Foldable Maybe -- Defined in `Data.Foldable'
instance Foldable ((,) a) -- Defined in `Data.Foldable'
...plus one other
...plus 29 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: foldr (\ x xs -> (replicate x x) ++ xs) []
In an equation for `echoes':
echoes = foldr (\ x xs -> (replicate x x) ++ xs) []
And then if I write it as follows, it works.
echoes lis = foldr (\ x xs -> (replicate x x) ++ xs) [] lis
I am confused about this and I think this is somehow related point free definitions of functions ?
Please clarify what the problem is there here.
The link from where I'm learning - https://en.wikibooks.org/wiki/Haskell/Lists_III
tl;dr
just always write explicit type signatures, then you're safe(r) from weird problems like that.
The reason this used to work but now doesn't is that foldr formerly had the signature
foldr :: (a -> b -> b) -> b -> [a] -> b
which is what the WikiBooks assumes, but in newer GHC it actually has the strictly more general signature
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
The old version is a special case of this, by simply choosing t ~ []. The reason they changed it is that you can also fold over other containers, such as arrays or maps. In fact, in your code
echoes = foldr (\ x xs -> (replicate x x) ++ xs) []
there's nothing the requires the input container to be a list, either, so it would in fact work perfectly well with the signature
echoes :: Foldable t => t Int -> [Int]
...of which, again, [Int] -> [Int] is a special case, so that function could then be used as
> echoes [1,2,3]
[1,2,2,3,3,3]
but also as
> echoes $ Data.Map.fromList [('a',2), ('c',5), ('b',1)]
[2,2,1,5,5,5,5,5]
Or you could have given the function the list-specific signature
echoes' :: [Int] -> [Int]
echoes' = foldr (\x xs -> (replicate x x) ++ xs) []
That works just the same on [1,2,3] but can't accept a Map.
The question is now, why does GHC not infer either of those signatures by itself? Well, if it had to choose one, it should be the more general Foldable version, because people might need to use this with other containers and wouldn't want to keep repeating the Foldable t => quantifier. However, this contradicts another Haskell rule, the monomorphism restriction. Because your echoes implementation doesn't explicitly accept any parameters (it only does that point-freely), it is a constant applicative form, and a standalone CAF is supposed to have monomorphic type unless explicitly specified to be polymorphic. Thus the error message you ran into: GHC really wants this to be monomorphic, but it has no information that restricts what concrete Foldable container to pick.
There are four ways around this:
As you noticed, by bringing the argument explicitly in scope, echoes is not a CAF anymore and therefore GHC infers the polymorphic type:
echoes'' l = foldr (\x xs -> (replicate x x) ++ xs) [] l
> :t echoes''echoes'' :: Foldable t => t Int -> [Int]
By disabling the monomorphism restriction, GHC won't care anymore whether it's CAF and just give it the more general type regardless:
{-# LANGUAGE NoMonomorphismRestriction #-}
echoes''' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoes'''echoes''' :: Foldable t => t Int -> [Int]
Discouraged If you turn on the -XExtendedDefaultingRules extension, GHC will automatically choose [] as the concrete monomorphic container for the CAF:
{-# LANGUAGE ExtendedDefaultRules #-}
echoes'''' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoes''''echoes'''' :: [Int] -> [Int]
GHCi has -XExtendedDefaultingRules enabled by default, so that's what also happens if you just declare the function in the GHCi prompt.
Strongly recommended If you explicitly specify the signature, you and GHC both know exactly what's intended and behave accordingly, without requiring any special GHC extensions.
echoes :: Foldable t => t Int -> [Int]
echoes = foldr (\x xs -> (replicate x x) ++ xs) []
echoes' :: [Int] -> [Int]
echoes' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoesechoes :: Foldable t => t Int -> [Int]> :t echoes'echoes' :: [Int] -> [Int]
The signature should be:
tailMay :: Foldable f => f a -> Maybe (f a)
I was given this definition of headMay, which makes sense, and is (to me) rather clever:
headMay :: Foldable f => f a -> Maybe a
headMay = foldr (const . Just) Nothing
Unfortunately, I haven't been able to think of anything similar for tailMay, and similar implementations seem to use magic or unsafe functions.
If this isn't possible, something that works for an (Applicative t, Foldable t) would also be fine for my purposes (which I gather would also imply Traversable t).
Tail only makes sense for list like structures, so it would be sensible to return a list, rather then the same container type f. Implementation is trivial and can be done in many ways, here is one.
import Control.Monad (guard)
import Data.Foldable (toList)
tailMay :: Foldable f => f a -> Maybe [a]
tailMay xs = guard (not (null xs)) >> Just (drop 1 (toList xs))
But, since it works for all list like structures, we could be cool and define it for all things that have is IsList.
{-# LANGUAGE TypeFamilies #-}
import Control.Monad (guard)
import Data.Foldable (toList)
import GHC.Exts (IsList(Item, fromList))
tailMay :: (IsList l, Item l ~ a, Foldable f) => f a -> Maybe l
tailMay xs = guard (not (null xs)) >> Just (fromList (drop 1 (toList xs)))
Edit
A nice benefit of the above solution is that you can change types from the argument to the result. For example you can start with a Vector and result with a Set. But that also has a down side, you need to specify the resulting type yourself:
So running this will result in a compile error:
λ> tailMay [1 :: Int]
<interactive>:24:1-18: error:
• Illegal equational constraint Item l ~ Int
(Use GADTs or TypeFamilies to permit this)
• When checking the inferred type
it :: forall l. (IsList l, Item l ~ Int) => Maybe l
λ> tailMay [1 :: Int] :: Maybe [Int]
Just []
We can easily restrict resulting type, if above is really a problem or simply an undesired effect for us:
tailMay :: (IsList l, Item l ~ a, Foldable f, l ~ f a) => f a -> Maybe l
After that it works great:
λ> import qualified Data.Vector as V
λ> tailMay $ V.enumFromTo 'a' 'd'
Just "bcd"
λ> tailMay [1 :: Int]
Just []
λ> tailMay V.empty
Nothing
λ> tailMay []
Nothing
Warning - It all would be great, but unfortunately there is no guarantee that fromList will have a total implementation:
λ> import qualified Data.List.NonEmpty as NE
λ> tailMay (5 NE.:| [] :: NE.NonEmpty Int)
Just *** Exception: NonEmpty.fromList: empty list
There is no general way to protect yourself from it, except by creating your own IsList like class and providing your own instances for all such data structures.
I'm not sure there is a right answer to this question, as things currently stand, though I wanted to expand on #DanielWagner's comment, as it is probably the most performant option, while also being safe:
-- | Law: (fromJust (length <$> tailMay l)) == (length tailMay) - 1,
-- if tailMay returns Just
class TailMay l where
tailMay :: l a -> Maybe (l a)
instance TailMay NonEmpty where
tailMay = LNE.nonEmpty . LNE.tail
instance TailMay [] where
tailMay = liftMay tail null -- <- from 'safe' package
-- from 'safe' package
liftMay :: (a -> b) -> (a -> Bool) -> (a -> Maybe b)
liftMay func test val = if test val then Nothing else Just $ func val
That said, ideally I'll get around to exploring #dfeuer's comment more as well.
Is there any explanation for why a lifted function, when applied to 2-tuple, only applies to the 2nd entry:
f x = x + 1
f <$> (2,2)
// -> (2,3)
On the other hand, tuples of any other length than 2 return errors.
Also
:t f <$>
returns an error. Is it possible to see the type of f <$> when acting on tuples?
Is there any explanation for that behaviour?
The Data.Tuple documentation is extremely brief and has no mention of how functions are lifted to tuples. Is there any source explaining it?
Update. A part of question about 2-tuples is related to this answer, where, however, the above question about multiple length tuples is not addressed.
One could (and arguably, GHC should) define a Functor instance for triples and larger tuples. To wit:
instance Functor ((,,) a b) where
fmap f (a, b, c) = (a, b, f c)
If this instance truly doesn't exist anywhere in base, I suspect that's mostly oversight, though I don't know the history well enough to say for sure. You can include this in any code where it seems useful, with the caveat that you should then absolutely put a fairly strict upper bound on the version of base in your *.cabal file, as this instance might reasonably be included in future versions of base. The PVP allows only the third component of the version to change in such a case, so include at least that many components in your upper bound!
Is there any explanation for why a lifted function, when applied to 2-tuple, only applies to the 2nd entry
Because tuples are heterogeneous which means that, in general, it would not make sense to try to apply a function of type b -> c to each component of a tuple of type (a, b).
If you want pairs of values of the same type, you can declare your own type Pair and then have the functor instance apply the function to each component.
data Pair a = Pair { fst :: a
, snd :: a }
instance Functor Pair where
fmap f (Pair fst snd) = Pair (f fst) (f snd)
Is it possible to see the type of f <$> when acting on tuples?
f <$> is a section (a partially applied infix operator). To get its type, you need to wrap it with parentheses like so:
:t (f <$>)
The Data.Tuple documentation is extremely brief and has no mention of how functions are lifted to tuples. Is there any source explaining it?
The combinator (<$>) (and (<*>)) are more general than just for tuples, you can find them in the Control.Applicative module.
All the other answers here seem pretty good, but I don't think anyone precisely answered your question yet.
I believe the reason 2-tuples (and no other tuples) are treated this way by default is because this allows them to be used in the same way as a Writer in a monadic context. (That is, ((,) a) and Writer are isomorphic.)
For example, given a function running in a Writer monad:
import Control.Monad.Writer
foo :: Int -> Writer [String] Int
foo n = do tell ["Running foo " ++ show n]
if (n <= 0) then do
tell ["We are done!"]
return 1
else do
rest <- foo (n-1)
return (n * rest)
you can rewrite it using the Monad instance of ((,) a):
bar :: Int -> ([String], Int)
bar n = do tell' ["Running bar " ++ show n]
if (n <= 0) then do
tell' ["We are done!"]
return 1
else do
rest <- bar (n-1)
return (n * rest)
where tell' str = (str, ())
and you'll find that these do the same thing:
runWriter (foo 5)
bar 5
up to the ordering of the pair.
The definition of tell' is only needed because ((,) a) hasn't been made an instance of MonadWriter for some reason.
(Edited to add:) While you could extend the definition to larger tuples, this doesn't really provide any additional generality over the definition for the pair: one component of the pair is a monoid to which you can write, and the other component is the underlying "value" in the monad context -- if you need more components for one or the other, you can just make the component a tuple itself.
In this answer, I will just expand a bit on one of the suggestions I made in a comment.
Is it possible to see the type of f <$> when acting on tuples?
(<$>) is a polymorphic function:
GHCi> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
With GHC 8, you can use the TypeApplications extension to specialise polymorphic functions by supplying instantiations of some or all of their type variables (in this case, f, a and b, in that order):
GHCi> :set -XTypeApplications
GHCi> :t (<$>) #Maybe
(<$>) #Maybe :: (a -> b) -> Maybe a -> Maybe b
GHCi> :t (<$>) #Maybe #Int
(<$>) #Maybe #Int :: (Int -> b) -> Maybe Int -> Maybe b
GHCi> :t (<$>) #Maybe #_ #Bool
(<$>) #Maybe #_ #Bool :: (t -> Bool) -> Maybe t -> Maybe Bool
GHCi> :t (<$>) #_ #Int #Bool
(<$>) #_ #Int #Bool
:: Functor t => (Int -> Bool) -> t Int -> t Bool
GHCi> :t (<$>) #Maybe #Int #Bool
(<$>) #Maybe #Int #Bool :: (Int -> Bool) -> Maybe Int -> Maybe Bool
To use that with pairs, use the prefix syntax for the pair type constructor:
GHCi> :t (<$>) #((,) _)
(<$>) #((,) _) :: (a -> b) -> (t, a) -> (t, b)
GHCi> -- You can use the specialised function normally.
GHCi> -- That includes passing arguments to it.
GHCi> f x = x + 1
GHCi> :t (<$>) #((,) _) f
(<$>) #((,) _) f :: Num b => (t, b) -> (t, b)
The _ in ((,) _) leaves it unspecified what the type of the first element of the pair (which is the first argument of the (,) type constructor) should be. Every choice of it gives rise to a different Functor. You can be more specific if you wish:
GHCi> :t (<$>) #((,) String) f
(<$>) #((,) String) f :: Num b => (String, b) -> (String, b)
Lastly, it is worth having a look at what happens if you try that with 3-tuples:
GHCi> :t (<$>) #((,,) _ _) f
(<$>) #((,,) _ _) f
:: (Num b, Functor ((,,) t t1)) => (t, t1, b) -> (t, t1, b)
As Daniel Wagner discusses in his answer, base doesn't define a Functor instance for 3-tuples. In spite of that, the type checker cannot exclude the possibility that someone somewhere might have defined an instance specific for some choice of the first two type parameters, however pointless that would be. For that reason, the speculative constraint Functor ((,,) t t1) shows up in the type (no such thing happens with pairs because there is a Functor ((,) a) instance in base). As expected, that blows up as soon as we try to instantiate the first two type parameters:
GHCi> :t (<$>) #((,,) Bool String) f
<interactive>:1:1: error:
• Could not deduce (Functor ((,,) Bool String))
arising from a use of ‘<$>’
from the context: Num b
bound by the inferred type of
it :: Num b => (Bool, String, b) -> (Bool, String, b)
at <interactive>:1:1
• In the expression: (<$>) #((,,) Bool String) f
I'm not a Haskell programmer, but I'm curious about the following questions.
Informal function specification:
Let MapProduct be a function that takes a function called F and multiple lists. It returns a list containing the results of calling F with one argument from each list in each possible combination.
Example:
Call MapProduct with F being a function that simply returns a list of its arguments, and two lists. One of the lists contains the integers 1 and 2, the other one contains the strings "a" and "b". It should return a list that contains the lists: 1 and "a", 1 and "b", 2 and "a", 2 and "b".
Questions:
How is MapProduct implemented?
What is the function's type? What is F's type?
Can one guess what the function does just by looking at its type?
Can you handle inhomogeneous lists as input? (e.g. 1 and "a" in one of the input lists)
What extra limitation (if any) do you need to introduce to implement MapProduct?
How is MapProduct implemented?
.
Prelude> :m + Control.Applicative
Prelude Control.Applicative> (,,) <$> [1,2,3] <*> ["a","b","c"] <*> [0.8, 1.2, 4.4]
[(1,"a",0.8),(1,"a",1.2),...,(3,"c",4.4)]
What is the function's type? What is F's type?
The type of F depends on the list you want to apply. <$> here is fmap, and (<*>) :: f(a->b) -> f a -> f b where f = [] here.
Can you handle inhomogeneous lists as input? (e.g. 1 and "a" in one of the input lists)
There is no such thing as an heterogeneous list. But you can simulate a heterogeneous list for a specific context with existential types. And then you can just use the method above to do the MapProduct.
*Main Control.Applicative> :i SB
data ShowBox where
SB :: forall s. (Show s) => s -> ShowBox
-- Defined at v.hs:1:35-36
*Main Control.Applicative> [SB 2, SB "a", SB 6.4]
[2,"a",6.4]
*Main Control.Applicative> (,) <$> [SB 2, SB "a", SB 6.4] <*> [SB 'z', SB 44]
[(2,'z'),(2,44),("a",'z'),("a",44),(6.4,'z'),(6.4,44)]
It is possible to define a function mapProduct that works for any arity of function:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
module MapProduct (
mapProduct
) where
import Control.Monad
newtype ProdFuncList a b = ProdFuncList [ a -> b ]
class MapProdResult p where
type MapProdArg p
apNext :: ProdFuncList x (MapProdArg p) -> [x] -> p
instance (MapProdResult b) => MapProdResult ([a] -> b) where
type MapProdArg ([a] -> b) = (a -> MapProdArg b)
apNext (ProdFuncList fs) = apNext . ProdFuncList . ap fs
instance MapProdResult [b] where
type MapProdArg [b] = b
apNext (ProdFuncList fs) = ap fs
mapProduct :: (MapProdResult q) => (a -> MapProdArg q) -> [a] -> q
mapProduct f = apNext (ProdFuncList [f])
Here it is in action:
> :l MapProduct.hs
[1 of 1] Compiling MapProduct ( MapProduct.hs, interpreted )
Ok, modules loaded: MapProduct.
> mapProduct (+10) [1..4] :: [Int]
[11,12,13,14]
> mapProduct (*) [1..4] [10..12] :: [Int]
[10,11,12,20,22,24,30,33,36,40,44,48]
> mapProduct (\a b c -> a:b:c:[]) "bcs" "ao" "dnt" :: [String]
["bad","ban","bat","bod","bon","bot","cad","can","cat","cod","con","cot","sad","san","sat","sod","son","sot"]
The downside of this approach is that you'll most likely have to type annotate the result (as shown in the examples above). It would be much more idiomatic to simply use fmap and ap directly:
> :m + Control.Monad
> (+10) `fmap` [1..4]
[11,12,13,14]
> (*) `fmap` [1..4] `ap` [10..12]
[10,11,12,20,22,24,30,33,36,40,44,48]
> (\a b c -> a:b:c:[]) `fmap` "bcs" `ap` "ao" `ap` "dnt"
["bad","ban","bat","bod","bon","bot","cad","can","cat","cod","con","cot","sad","san","sat","sod","son","sot"]
This requires no type annotations, and is fully general over all monads, not just [].
(The MapProduct module above could easily be generalized over all monads as well. I didn't so as to make it clearly solve the original question.)
The function that you describe is closely related to the zipWithN functions. It will have the same type - it will just result in bigger result lists. Now the problem is that there is no way to express "a function that takes N arguments of the types t_1, ..., t_n" or "n lists of the types [t_1],...,[t_n]" (or "an n-tuple of type ([t_1], ..., [t_n]") ) in haskell's type system (without extensions like template haskell). This is why there is not one zipWith function, but one for each number of arguments lists that is supported.
So to answer your questions:
It is implemented by defining a function mapProductN for every number N that you want to support. For N=2 it would look like this:
mapProduct f l1 l2 = [f x1 x2 | x1 <- l1, x2 <- x2]
Or as a general blueprint (i.e. pseudo-code) how to define the functions for any N:
mapProduct f l1 ... ln = [f x1 ... xn | x1 <- l1, ..., xn <- ln]
As I said it's the same as the types of the zipWith functions, i.e.:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
Since f is the first argument to the function, the type of the first argument is f's type (so for n=2 it'd be a -> b -> c)
Well since it has the same type as zipWith and zipWith does something else, that'd be a no.
Haskell doesn't allow inhomogeneous lists without an extension.
There's an upper limit on the number of lists unless you're willing to spend the time writing infinite versions of mapProduct.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
What are the lesser-known but useful features of the Haskell programming language. (I understand the language itself is lesser-known, but work with me. Even explanations of the simple things in Haskell, like defining the Fibonacci sequence with one line of code, will get upvoted by me.)
Try to limit answers to the Haskell core
One feature per answer
Give an example and short description of the feature, not just a link to documentation
Label the feature using bold title as the first line
User-defined control structures
Haskell has no shorthand ternary operator. The built-in if-then-else is always ternary, and is an expression (imperative languages tend to have ?:=expression, if=statement). If you want, though,
True ? x = const x
False ? _ = id
will define (?) to be the ternary operator:
(a ? b $ c) == (if a then b else c)
You'd have to resort to macros in most other languages to define your own short-circuiting logical operators, but Haskell is a fully lazy language, so it just works.
-- prints "I'm alive! :)"
main = True ? putStrLn "I'm alive! :)" $ error "I'm dead :("
Hoogle
Hoogle is your friend. I admit, it's not part of the "core", so cabal install hoogle
Now you know how "if you're looking for a higher-order function, it's already there" (ephemient's comment). But how do you find that function? With hoogle!
$ hoogle "Num a => [a] -> a"
Prelude product :: Num a => [a] -> a
Prelude sum :: Num a => [a] -> a
$ hoogle "[Maybe a] -> [a]"
Data.Maybe catMaybes :: [Maybe a] -> [a]
$ hoogle "Monad m => [m a] -> m [a]"
Prelude sequence :: Monad m => [m a] -> m [a]
$ hoogle "[a] -> [b] -> (a -> b -> c) -> [c]"
Prelude zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
The hoogle-google programmer is not able to write his programs on paper by himself the same way he does with the help of the computer. But he and the machine together are a forced not* to be reckoned with.
Btw, if you liked hoogle be sure to check out hlint!
My brain just exploded
If you try to compile this code:
{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Foo a
ignorefoo f = 1 where Foo a = f
You will get this error message:
$ ghc Foo.hs
Foo.hs:3:22:
My brain just exploded.
I can't handle pattern bindings for existentially-quantified constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.
In the binding group for
Foo a
In a pattern binding: Foo a = f
In the definition of `ignorefoo':
ignorefoo f = 1
where
Foo a = f
Free Theorems
Phil Wadler introduced us to the notion of a free theorem and we've been abusing them in Haskell ever since.
These wonderful artifacts of Hindley-Milner-style type systems help out with equational reasoning by using parametricity to tell you about what a function will not do.
For instance, there are two laws that every instance of Functor should satisfy:
forall f g. fmap f . fmap g = fmap (f . g)
fmap id = id
But, the free theorem tells us we need not bother proving the first one, but given the second it comes for 'free' just from the type signature!
fmap :: Functor f => (a -> b) -> f a -> f b
You need to be a bit careful with laziness, but this is partially covered in the original paper, and in Janis Voigtlaender's more recent paper on free theorems in the presence of seq.
Shorthand for a common list operation
The following are equivalent:
concat $ map f list
concatMap f list
list >>= f
Edit
Since more details were requested...
concat :: [[a]] -> [a]
concat takes a list of lists and concatenates them into a single list.
map :: (a -> b) -> [a] -> [b]
map maps a function over a list.
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap is equivalent to (.) concat . map: map a function over a list, and concatenate the results.
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
A Monad has a bind operation, which is called >>= in Haskell (or its sugared do-equivalent). List, aka [], is a Monad. If we substitute [] for m in the above:
instance Monad [] where
(>>=) :: [a] -> (a -> [b]) -> [b]
return :: a -> [a]
What's the natural thing for the Monad operations to do on a list? We have to satisfy the monad laws,
return a >>= f == f a
ma >>= (\a -> return a) == ma
(ma >>= f) >>= g == ma >>= (\a -> f a >>= g)
You can verify that these laws hold if we use the implementation
instance Monad [] where
(>>=) = concatMap
return = (:[])
return a >>= f == [a] >>= f == concatMap f [a] == f a
ma >>= (\a -> return a) == concatMap (\a -> [a]) ma == ma
(ma >>= f) >>= g == concatMap g (concatMap f ma) == concatMap (concatMap g . f) ma == ma >>= (\a -> f a >>= g)
This is, in fact, the behavior of Monad []. As a demonstration,
double x = [x,x]
main = do
print $ map double [1,2,3]
-- [[1,1],[2,2],[3,3]]
print . concat $ map double [1,2,3]
-- [1,1,2,2,3,3]
print $ concatMap double [1,2,3]
-- [1,1,2,2,3,3]
print $ [1,2,3] >>= double
-- [1,1,2,2,3,3]
Nestable multiline comments.
{- inside a comment,
{- inside another comment, -}
still commented! -}
Generalized algebraic data types. Here's an example interpreter where the type system lets you cover all the cases:
{-# LANGUAGE GADTs #-}
module Exp
where
data Exp a where
Num :: (Num a) => a -> Exp a
Bool :: Bool -> Exp Bool
Plus :: (Num a) => Exp a -> Exp a -> Exp a
If :: Exp Bool -> Exp a -> Exp a -> Exp a
Lt :: (Num a, Ord a) => Exp a -> Exp a -> Exp Bool
Lam :: (a -> Exp b) -> Exp (a -> b) -- higher order abstract syntax
App :: Exp (a -> b) -> Exp a -> Exp b
-- deriving (Show) -- failse
eval :: Exp a -> a
eval (Num n) = n
eval (Bool b) = b
eval (Plus e1 e2) = eval e1 + eval e2
eval (If p t f) = eval $ if eval p then t else f
eval (Lt e1 e2) = eval e1 < eval e2
eval (Lam body) = \x -> eval $ body x
eval (App f a) = eval f $ eval a
instance Eq a => Eq (Exp a) where
e1 == e2 = eval e1 == eval e2
instance Show (Exp a) where
show e = "<exp>" -- very weak show instance
instance (Num a) => Num (Exp a) where
fromInteger = Num
(+) = Plus
Patterns in top-level bindings
five :: Int
Just five = Just 5
a, b, c :: Char
[a,b,c] = "abc"
How cool is that! Saves you that call to fromJust and head every now and then.
Optional Layout
You can use explicit braces and semicolons instead of whitespace (aka layout) to delimit blocks.
let {
x = 40;
y = 2
} in
x + y
... or equivalently...
let { x = 40; y = 2 } in x + y
... instead of ...
let x = 40
y = 2
in x + y
Because layout is not required, Haskell programs can be straightforwardly produced by other programs.
seq and ($!) only evaluate enough to check that something is not bottom.
The following program will only print "there".
main = print "hi " `seq` print "there"
For those unfamiliar with Haskell, Haskell is non-strict in general, meaning that an argument to a function is only evaluated if it is needed.
For example, the following prints "ignored" and terminates with success.
main = foo (error "explode!")
where foo _ = print "ignored"
seq is known to change that behavior by evaluating to bottom if its first argument is bottom.
For example:
main = error "first" `seq` print "impossible to print"
... or equivalently, without infix ...
main = seq (error "first") (print "impossible to print")
... will blow up with an error on "first". It will never print "impossible to print".
So it might be a little surprising that even though seq is strict, it won't evaluate something the way eager languages evaluate. In particular, it won't try to force all the positive integers in the following program. Instead, it will check that [1..] isn't bottom (which can be found immediately), print "done", and exit.
main = [1..] `seq` print "done"
Operator Fixity
You can use the infix, infixl or infixr keywords to define operators associativity and precedence. Example taken from the reference:
main = print (1 +++ 2 *** 3)
infixr 6 +++
infixr 7 ***,///
(+++) :: Int -> Int -> Int
a +++ b = a + 2*b
(***) :: Int -> Int -> Int
a *** b = a - 4*b
(///) :: Int -> Int -> Int
a /// b = 2*a - 3*b
Output: -19
The number (0 to 9) after the infix allows you to define the precedence of the operator, being 9 the strongest. Infix means no associativity, whereas infixl associates left and infixr associates right.
This allows you to define complex operators to do high level operations written as simple expressions.
Note that you can also use binary functions as operators if you place them between backticks:
main = print (a `foo` b)
foo :: Int -> Int -> Int
foo a b = a + b
And as such, you can also define precedence for them:
infixr 4 `foo`
Avoiding parentheses
The (.) and ($) functions in Prelude have very convenient fixities, letting you avoid parentheses in many places. The following are equivalent:
f (g (h x))
f $ g $ h x
f . g $ h x
f . g . h $ x
flip helps too, the following are equivalent:
map (\a -> {- some long expression -}) list
flip map list $ \a ->
{- some long expression -}
Pretty guards
Prelude defines otherwise = True, making complete guard conditions read very naturally.
fac n
| n < 1 = 1
| otherwise = n * fac (n-1)
C-Style Enumerations
Combining top-level pattern matching and arithmetic sequences gives us a handy way to define consecutive values:
foo : bar : baz : _ = [100 ..] -- foo = 100, bar = 101, baz = 102
Readable function composition
Prelude defines (.) to be mathematical function composition; that is, g . f first applies f, then applies g to the result.
If you import Control.Arrow, the following are equivalent:
g . f
f >>> g
Control.Arrow provides an instance Arrow (->), and this is nice for people who don't like to read function application backwards.
let 5 = 6 in ... is valid Haskell.
Infinite Lists
Since you mentioned fibonacci, there is a very elegant way of generating fibonacci numbers from an infinite list like this:
fib#(1:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]
The # operator allows you to use pattern matching on the 1:tfib structure while still referring to the whole pattern as fib.
Note that the comprehension list enters an infinite recursion, generating an infinite list. However, you can request elements from it or operate them, as long as you request a finite amount:
take 10 fib
You can also apply an operation to all elements before requesting them:
take 10 (map (\x -> x+1) fib)
This is thanks to Haskell's lazy evaluation of parameters and lists.
Flexible specification of module imports and exports
Importing and exporting is nice.
module Foo (module Bar, blah) -- this is module Foo, export everything that Bar expored, plus blah
import qualified Some.Long.Name as Short
import Some.Long.Name (name) -- can import multiple times, with different options
import Baz hiding (blah) -- import everything from Baz, except something named 'blah'
If you're looking for a list or higher-order function, it's already there
There's sooo many convenience and higher-order functions in the standard library.
-- factorial can be written, using the strict HOF foldl':
fac n = Data.List.foldl' (*) 1 [1..n]
-- there's a shortcut for that:
fac n = product [1..n]
-- and it can even be written pointfree:
fac = product . enumFromTo 1
Equational Reasoning
Haskell, being purely functional allows you to read an equal sign as a real equal sign (in the absence of non-overlapping patterns).
This allows you to substitute definitions directly into code, and in terms of optimization gives a lot of leeway to the compiler about when stuff happens.
A good example of this form of reasoning can be found here:
http://www.haskell.org/pipermail/haskell-cafe/2009-March/058603.html
This also manifests itself nicely in the form of laws or RULES pragmas expected for valid members of an instance, for instance the Monad laws:
returrn a >>= f == f a
m >>= return == m
(m >>= f) >>= g == m >>= (\x -> f x >>= g)
can often be used to simplify monadic code.
Laziness
Ubiquitous laziness means you can do things like define
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
But it also provides us with a lot of more subtle benefits in terms of syntax and reasoning.
For instance, due to strictness ML has to deal with the value restriction, and is very careful to track circular let bindings, but in Haskell, we can let every let be recursive and have no need to distinguish between val and fun. This removes a major syntactic wart from the language.
This indirectly gives rise to our lovely where clause, because we can safely move computations that may or may not be used out of the main control flow and let laziness deal with sharing the results.
We can replace (almost) all of those ML style functions that need to take () and return a value, with just a lazy computation of the value. There are reasons to avoid doing so from time to time to avoid leaking space with CAFs, but such cases are rare.
Finally, it permits unrestricted eta-reduction (\x -> f x can be replaced with f). This makes combinator oriented programming for things like parser combinators much more pleasant than working with similar constructs in a strict language.
This helps you when reasoning about programs in point-free style, or about rewriting them into point-free style and reduces argument noise.
Parallel list comprehension
(Special GHC-feature)
fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]
Enhanced pattern matching
Lazy patterns
Irrefutable patterns
let ~(Just x) = someExpression
See pattern matching
Enumerations
Any type which is an instance of Enum can be used in an arithmetic sequence, not just numbers:
alphabet :: String
alphabet = ['A' .. 'Z']
Including your own datatypes, just derive from Enum to get a default implementation:
data MyEnum = A | B | C deriving(Eq, Show, Enum)
main = do
print $ [A ..] -- prints "[A,B,C]"
print $ map fromEnum [A ..] -- prints "[0,1,2]"
Monads
They are not that hidden, but they are simply everywhere, even where you don't think of them (Lists, Maybe-Types) ...