Difference between revisions of "Common Misunderstandings"
(Type signature binding) 
m (fix gmane link using archive.is) 

(10 intermediate revisions by 5 users not shown)  
Line 5:  Line 5:  
== Indentation == 
== Indentation == 

−  Perhaps the first tripup  you might understand that indentation defines where a code block starts and the lack of an equal amount of indentation indicates the previous code block ended. What some miss is that <hask>then</hask> and <hask>else</hask> must be indented deeper than the <hask>if</hask> statement: 
+  Perhaps the first tripup  you might understand that indentation defines where a code block starts and the lack of an equal amount of indentation indicates the previous code block ended. What some miss is that <hask>then</hask> and <hask>else</hask>, if used within a section of code where indentation matters, must be indented deeper than the <hask>if</hask> statement. That is, the position of the "else" in the following is a syntax error: 
<haskell> 
<haskell> 

−  if boolean 
+  do if boolean then 
−  +  expr1 

−  else 
+  else 
+  expr2 

+  </Haskell> 

+  
+  The following is correct: 

+  
+  <haskell> 

+  do if boolean 

+  then expr1 

+  else expr2 

</haskell> 
</haskell> 

Line 18:  Line 18:  
if boolean then expr1 else expr2 
if boolean then expr1 else expr2 

</haskell> 
</haskell> 

+  
+  Fortunately this misfeature is fixed in [[Haskell 2010]], and the first snippet is valid in compilers that support it (e.g. GHC 7.0.1 and later). 

== If / Then / Else == 
== If / Then / Else == 

Line 26:  Line 28:  
</code> 
</code> 

−  The above is valid, 
+  The above is valid C code, a [http://en.wikipedia.org/wiki/Ternary_operation ternary operation], that's used quite commonly as a shortcut to typing out a full ifthenelse block. It states that if <code>b</code> is true then <code>x = y</code> otherwise <code>x = z</code>. Notice how this makes no sense without <code>z</code>. Similarly, in Haskell an <hask>if</hask>/<hask>then</hask> makes no sense without an <hask>else</hask>. 
<haskell> 
<haskell> 

Line 32:  Line 34:  
</haskell> 
</haskell> 

−  What is <code>x</code> when <code>b</code> is false? One should also recognize that the types returned by the <hask>then</hask> and <hask>else</hask> branches must match due to 
+  What is <code>x</code> when <code>b</code> is false? One should also recognize that the types returned by the <hask>then</hask> and <hask>else</hask> branches must match due to Haskell's strong and static type system. 
When <hask>if</hask> is used for sequencing IO it is not uncommon to see an <hask>else</hask> that returns a null value: 
When <hask>if</hask> is used for sequencing IO it is not uncommon to see an <hask>else</hask> that returns a null value: 

Line 62:  Line 64:  
</haskell> 
</haskell> 

−  This works fine when the list stands alone, but when applying a 
+  This works fine when the list stands alone, but when applying a function take note the function application binds stronger than the type signature. This means the below signature is invalid: 
<haskell> 
<haskell> 

Line 71:  Line 73:  
<haskell> 
<haskell> 

ghci> map floor ([1,2,3,4] :: [Float]) 
ghci> map floor ([1,2,3,4] :: [Float]) 

+  </haskell> 

+  
+  or alternatively put the type signature on one of the elements: 

+  
+  <haskell> 

+  ghci> map floor [1,2,3,4::Float] 

</haskell> 
</haskell> 

Line 79:  Line 87:  
== Iterating Over a List == 
== Iterating Over a List == 

−  Some beginners 
+  Some beginners confuse a singleelement list pattern (such as <hask>[x]</hask>) with a pattern that iterates over every element in the list. 
One example that recently (in April, 2008) appeared on the HaskellCafe mailing list (see the reply post [http://www.haskell.org/pipermail/haskellcafe/2008April/041701.html Re: Embedding newlines into a string?]) was the following. Here, one coder attempted to write a function <hask>hanoi</hask> to solve the Towers of Hanoi problem, but to code it so that each tower could be named polymorphically, using, for example, either Chars or Ints. The problematic code segment was the following: 
One example that recently (in April, 2008) appeared on the HaskellCafe mailing list (see the reply post [http://www.haskell.org/pipermail/haskellcafe/2008April/041701.html Re: Embedding newlines into a string?]) was the following. Here, one coder attempted to write a function <hask>hanoi</hask> to solve the Towers of Hanoi problem, but to code it so that each tower could be named polymorphically, using, for example, either Chars or Ints. The problematic code segment was the following: 

Line 104:  Line 112:  
The coder tried to run the code in WinHugs as follows: 
The coder tried to run the code in WinHugs as follows: 

−  <hask> 

+  <haskell> 

Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2)) 
Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2)) 

−  </ 
+  </haskell> 
However, this was the result: 
However, this was the result: 

−  <hask> 

+  <haskell> 

Program error: pattern match failure: hanoi_shower 
Program error: pattern match failure: hanoi_shower 

[('a','b'),('a','c')] ++ ([] ++ hanoi 'b' 'a' 'c' (2  1)) 
[('a','b'),('a','c')] ++ ([] ++ hanoi 'b' 'a' 'c' (2  1)) 

−  </ 
+  </haskell> 
The problem was that the parameter <hask>[(a, b)]</hask> to <hask>hanoi_shower</hask> only matched the first element of the list, but didn't iterate over the list as intended. 
The problem was that the parameter <hask>[(a, b)]</hask> to <hask>hanoi_shower</hask> only matched the first element of the list, but didn't iterate over the list as intended. 

Line 130:  Line 138:  
Here is the result of executing the above code in WinHugs: 
Here is the result of executing the above code in WinHugs: 

−  <hask> 

+  <haskell> 

Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2)) 
Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2)) 

Move 'a' to 'b'. 
Move 'a' to 'b'. 

Line 140:  Line 148:  
Move 1 to 3. 
Move 1 to 3. 

Move 2 to 3. 
Move 2 to 3. 

−  </ 
+  </haskell> 
Notice that since <hask>a</hask> and <hask>b</hask> in <hask>(a, b)</hask> are polymorphic types, they can range over both <hask>Chars</hask> and <hask>Ints</hask>. 
Notice that since <hask>a</hask> and <hask>b</hask> in <hask>(a, b)</hask> are polymorphic types, they can range over both <hask>Chars</hask> and <hask>Ints</hask>. 

Line 175:  Line 183:  
== Guards == 
== Guards == 

−  (The following two tips on guards were contributed by the user 7stud in the thread "Top beginner mistakes" (see http://article.gmane.org/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).) 
+  (The following two tips on guards were contributed by the user 7stud in the thread "Top beginner mistakes" (see https://web.archive.org/web/20180127010825/http://article.gmane.org:80/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).) 
Some beginners make the mistake of putting an equals sign after a function name when using guards; ''viz.'': 
Some beginners make the mistake of putting an equals sign after a function name when using guards; ''viz.'': 

Line 214:  Line 222:  
== Parentheses == 
== Parentheses == 

−  (The following tip on parentheses was contributed by the user 7stud in the thread "Top beginner mistakes" (see http://article.gmane.org/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).) 
+  (The following tip on parentheses was contributed by the user 7stud in the thread "Top beginner mistakes" (see https://web.archive.org/web/20180127010825/http://article.gmane.org:80/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).) 
Some beginners make the mistake of not putting parentheses around arguments of the form x:xs; ''viz.'': 
Some beginners make the mistake of not putting parentheses around arguments of the form x:xs; ''viz.'': 

Line 232:  Line 240:  
(Entry added by [[User:DekuDekuplexBenjamin L. Russell]].) 
(Entry added by [[User:DekuDekuplexBenjamin L. Russell]].) 

+  
+  
+  = See also = 

+  
+  * [[What a Monad is not]] 
Latest revision as of 15:16, 4 August 2019
Contents
Common Mistakes and Incorrect Beliefs By Haskell Beginners
People going from zero to Haskell are likely to gain a misunderstanding or miss a point that isn't stressed enough. Here are some mistakes that have been observed from multiple sources.
Indentation
Perhaps the first tripup  you might understand that indentation defines where a code block starts and the lack of an equal amount of indentation indicates the previous code block ended. What some miss is that then
and else
, if used within a section of code where indentation matters, must be indented deeper than the if
statement. That is, the position of the "else" in the following is a syntax error:
do if boolean then
expr1
else
expr2
The following is correct:
do if boolean
then expr1
else expr2
Or they can be on the same line as the if:
if boolean then expr1 else expr2
Fortunately this misfeature is fixed in Haskell 2010, and the first snippet is valid in compilers that support it (e.g. GHC 7.0.1 and later).
If / Then / Else
ifthen statements must always include an 'else' portion. It might be best not to think of ifthenelse as flow control, as in most imperative languages, but think of it as construction of a value using a well formed expression.
x = b ? y : z;
The above is valid C code, a ternary operation, that's used quite commonly as a shortcut to typing out a full ifthenelse block. It states that if b
is true then x = y
otherwise x = z
. Notice how this makes no sense without z
. Similarly, in Haskell an if
/then
makes no sense without an else
.
let x = if b then y  compare to x = b ? y
What is x
when b
is false? One should also recognize that the types returned by the then
and else
branches must match due to Haskell's strong and static type system.
When if
is used for sequencing IO it is not uncommon to see an else
that returns a null value:
main = do
startNetwork < askUser "Network? "
if startNetwork
then do iface < initNetworkInterface
handlePackets iface
else return ()
Such uses can be more succinct if they use the when
function (from the Control.Monad module):
main = do
startNetwork < askUser "Network? "
when startNetwork $ do
iface < initNetworkInterface
handlePackets iface
Binding of Type Signatures
Due to the typeclass overloading of numbers, lists of numbers are often typed as:
ghci> [1,2,3,4] :: [Float]
This works fine when the list stands alone, but when applying a function take note the function application binds stronger than the type signature. This means the below signature is invalid:
ghci> map floor [1,2,3,4] :: [Float]
and should instead be:
ghci> map floor ([1,2,3,4] :: [Float])
or alternatively put the type signature on one of the elements:
ghci> map floor [1,2,3,4::Float]
do Notation
If the do notation page ever exists I'll advise you to check it out. Until then, understand that a missing do
from the top of a function or code block can result in your compiler giving an error message citing a much later line number. Also, any new blocks (ex: from an if
or case
) must have their own do
, even if the higher level code block already had one.
Sorry this isn't the full picture  for an inverse point of view see do notation considered harmful.
Iterating Over a List
Some beginners confuse a singleelement list pattern (such as [x]
) with a pattern that iterates over every element in the list.
One example that recently (in April, 2008) appeared on the HaskellCafe mailing list (see the reply post Re: Embedding newlines into a string?) was the following. Here, one coder attempted to write a function hanoi
to solve the Towers of Hanoi problem, but to code it so that each tower could be named polymorphically, using, for example, either Chars or Ints. The problematic code segment was the following:
hanoi_shower :: Show a => [(a, a)] > String
hanoi_shower [(a, b)] = "Move " ++ show a ++ " to " ++ show b ++ "."
in the following program:
hanoi :: a > a > a > Int > [(a, a)]
hanoi source using dest n
 n == 1 = [(source, dest)]
 otherwise = hanoi source dest using (n1)
++ hanoi source using dest 1
++ hanoi using source dest (n1)
hanoi_shower :: Show a => [(a, a)] > String
hanoi_shower [(a, b)] = "Move " ++ show a ++ " to " ++ show b ++ "."
The coder tried to run the code in WinHugs as follows:
Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2))
However, this was the result:
Program error: pattern match failure: hanoi_shower
[('a','b'),('a','c')] ++ ([] ++ hanoi 'b' 'a' 'c' (2  1))
The problem was that the parameter [(a, b)]
to hanoi_shower
only matched the first element of the list, but didn't iterate over the list as intended.
Here is a corrected version of the code above:
hanoi_shower :: Show a => [(a, a)] > String
hanoi_shower moves = unlines ["Move " ++ show a ++ " to "++ show b ++ "."  (a, b) < moves]
Here, moves
is patternmatched to type [(a, a)]
(a list of pairs). The problem is how to iterate over the elements (pairs) of the list while separating the first a
of each pair from the second a
.
The solution above uses list comprehension: The generator (a, b) < moves
feeds each pair in turn to the lefthand expression (a, b)
, and this pair is mapped to the left expression, "Move " ++ show a ++ " to "++ show b ++ "."
, building a new list of sentences representing moves. Then, the function unlines
breaks this list into a sequence of lines.
Here is the result of executing the above code in WinHugs:
Main> putStr (hanoi_shower (hanoi 'a' 'b' 'c' 2))
Move 'a' to 'b'.
Move 'a' to 'c'.
Move 'b' to 'c'.
Main> putStr (hanoi_shower (hanoi 1 2 3 2))
Move 1 to 2.
Move 1 to 3.
Move 2 to 3.
Notice that since a
and b
in (a, b)
are polymorphic types, they can range over both Chars
and Ints
.
Another way of writing hanoi_shower
, using map
, is as follows:
hanoi_shower :: Show a => [(a, a)] > String
hanoi_shower moves = unlines (map move moves)
where move (a, b) = "Move " ++ show a ++ " to "++ show b ++ "."
Here, move
is mapped over moves
, and each pair (a, b)
of moves
is patternmatched against "Move " ++ show a ++ " to "++ show b ++ "."
Another way to map over a list is to use recursion, although this method is considered nonidiomatic Haskell (Haskellers generally prefer using higherorder functions over recursion when possible):
hanoi :: a > a > a > Int > [(a, a)]
hanoi source using dest n
 n == 0 = []
 n == 1 = [(source, dest)]
 otherwise = hanoi source dest using (n1)
++ hanoi source using dest 1
++ hanoi using source dest (n1)
hanoi_shower :: Show a => [(a, a)] > String
hanoi_shower [] = ""
hanoi_shower ((a, b):moves) = unlines ["Move " ++ show a ++ " to "++ show b ++ "."] ++ hanoi_shower moves
Here, in hanoi_shower
, the base case is simply an empty list []
. At each recursive step, a list of type [(a, a)]
(a list of pairs) is mapped against the parameter (a, b):moves
of hanoi_shower
. This separates the head of the list (a, b)
from the tail of the list moves
, which then is further matched against ((a, b):moves)
on the next recursive call.
There are other ways of iterating over lists as well. One advantage of Haskell is that there are often many ways of performing the same action, including iterating over lists.
Guards
(The following two tips on guards were contributed by the user 7stud in the thread "Top beginner mistakes" (see https://web.archive.org/web/20180127010825/http://article.gmane.org:80/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).)
Some beginners make the mistake of putting an equals sign after a function name when using guards; viz.:
myfunction x y =
 x < 2 = "a"
 y > 20 = "b"
 otherwise = "c"
This causes a cryptic error message similar to the following to be displayed:
dhask.hs:2:4: parse error on input `' Failed, modules loaded: none.
Another common mistake that some beginners make is writing "if" in front of the guard conditions; viz.:
myfunction x y
 if x < 2 = "a"
 if y > 20 = "b"
 otherwise = "c"
This causes a mysterious error message similar to the following to be shown:
dhask.hs:2:25: parse error on input `=' Failed, modules loaded: none.
In both cases, the error messages don't help to identify the problem.
(Entry added by Benjamin L. Russell.)
Parentheses
(The following tip on parentheses was contributed by the user 7stud in the thread "Top beginner mistakes" (see https://web.archive.org/web/20180127010825/http://article.gmane.org:80/gmane.comp.lang.haskell.beginners/1121) on the HaskellBeginners mailing list on Wed, 4 Mar 2009 21:54:05 +0000 (UTC).)
Some beginners make the mistake of not putting parentheses around arguments of the form x:xs; viz.:
dosomething x:xs = head xs
This causes an ambiguous error message similar to the following to be shown:
dhask.hs:1:0: Parse error in pattern Failed, modules loaded: none.
Here, the error message doesn't help to recognize the problem.
(Entry added by Benjamin L. Russell.)