Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I'm really confused about function declarations in Haskell

This is a homework, so I would prefer only tips or a link to where I can learn rather than a full answer. This is what I am given:

allEqual :: Eq a => a -> a -> a -> Bool

What I understand from this is that I am supposed to compare 3 values (in this case a, a, a?) and return whether or not they all equal one another. This is what I tried:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
  Bool check <- x == y
  Bool nextC <- y == z
  if check == nextC
    then True
    else False

I honestly feel completely lost with Haskell, so any tips on how to read functions or declare them would help immensely.

like image 358
Nico Arevalo Avatar asked Apr 30 '20 09:04

Nico Arevalo


People also ask

What is function declaration Haskell?

Like other languages, Haskell does have its own functional definition and declaration. Function declaration consists of the function name and its argument list along with its output. Function definition is where you actually define a function.

What is <> called in Haskell?

It's an alias for mappend , from the Data. Monoid module.

Does Haskell pass by reference?

No, it's not possible, because Haskell variables are immutable, therefore, the creators of Haskell must have reasoned there's no point of passing a reference that cannot be changed.

Does Haskell have Elif?

Note that Haskell does not have an "elif" statement like Python.


1 Answers

This question already has several other perfectly good answers explaining how to solve your problem. I don’t want to do that; instead, I will go through each line of your code, progressively correct the problems, and hopefully help you understand Haskell a bit better.

First, I’ll copy your code for convenience:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
  Bool check <- x == y
  Bool nextC <- y == z
  if check == nextC
    then True
    else False

The first line is the type signature; this is already explained well in other answers, so I’ll skip this and go on to the next line.

The second line is where you are defining your function. The first thing you’ve missed is that you need an equals sign to define a function: function definition syntax is functionName arg1 arg2 arg3 … = functionBody, and you can’t remove the =. So let’s correct that:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = do
  Bool check <- x == y
  Bool nextC <- y == z
  if check == nextC
    then True
    else False

The next error is using do notation. do notation is notorious for confusing beginners, so don’t feel bad about misusing it. In Haskell, do notation is only used in specific situations where it is necessary to execute a sequence of statements line by line, and especially when you have some side-effect (like, say, printing to the console) which is executed with each line. Clearly, this doesn’t fit here — all you’re doing is comparing some values and returning a result, which is hardly something which requires line-by-line execution. So let’s get rid of that do:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
  let Bool check = x == y
      Bool nextC = y == z
  in
    if check == nextC
      then True
      else False

(I’ve also replaced the <- binding with let … in …, since <- can only be used within a do block.)

Next, another problem: Bool check is not valid Haskell! You may be familiar with this syntax from other languages, but in Haskell, a type is always specified using ::, and often with a type signature. So I’ll remove Bool before the names and add type signatures instead:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
  let check :: Bool
      check = x == y
      nextC :: Bool
      nextC = y == z
  in
    if check == nextC
      then True
      else False

Now, at this point, your program is perfectly valid Haskell — you’ll be able to compile it, and it will work. But there’s still a few improvements you can make.

For a start, you don’t need to include types — Haskell has type inference, and in most cases it’s fine to leave types out (although it’s traditional to include them for functions). So let’s get rid of the types in the let:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
  let check = x == y
      nextC = y == z
  in
    if check == nextC
      then True
      else False

Now, check and nextC are only used in one place — giving them names doesn’t do anything, and only serves to make the code less readable. So I’ll inline the definitions of check and nextC into their usages:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
  if (x == y) == (y == z)
    then True
    else False

Finally, I see you have an expression of the form if <condition> then True else False. This is redundant — you can simply return the <condition> with the same meaning. So let’s do that:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = (x == y) == (y == z)

This is much, much better than the code you started with!

(There is actually one more improvement that you can make to this code. At this point, it should be obvious that your code has a bug. Can you find it? And if so, can you fix it? Hint: you can use the && operator to ‘and’ two booleans together.)

like image 106
bradrn Avatar answered Nov 06 '22 06:11

bradrn