Skip to content

Instantly share code, notes, and snippets.

@veslav3
Last active October 29, 2017 16:33
Show Gist options
  • Save veslav3/fce83a6b0dc74aa890ebead3ae988327 to your computer and use it in GitHub Desktop.
Save veslav3/fce83a6b0dc74aa890ebead3ae988327 to your computer and use it in GitHub Desktop.

Day one logical with Haskell

Today I started reading the Haskell chapter in the seven languages in seven weeks book. (https://pragprog.com/book/btlang/seven-languages-in-seven-weeks) In this blog series I will report my findings about the language and focus on the differences with languages like Java and C#.

Discovering the language

One interesting thing that I noticed is that string concatenation happens using a double plus sign:

Prelude> "hello" ++ " world"
"hello world"

In order to concatenate characters together you will need to define a character array, this will result in a string.

Haskell uses a slash instead of an exclamation mark to evaluate if an expression is false:

Prelude> (5 + 5) /= 10
False

Everything in Haskell is a function, this means that the if statement has a return value. This means an error is thrown when this function is misused:

Prelude> if 1 then "true" else "false"

<interactive>:11:4: error:
    * No instance for (Num Bool) arising from the literal `1'
    * In the expression: 1
      In the expression: if 1 then "true" else "false"
      In an equation for `it': it = if 1 then "true" else "false"

It's fairly easy to define a function in haskell:

Prelude> let double x = x * 2
Prelude> double 2
4

Using modules

Defining a function in a file (module) is also easy:

// double.hs
module Main where
  main = undefined
  
  double x = x + x

I think defining a function like this is way more elegant for this use case than java. In java you would define a function like this as follows:

public int double (int x) {
    return x = x + x;
}

In the java implementation it is definitely better to see what the function is going to do, because you see the return type and the input more clearly. However java restricts itself to a certain type, for example an int or a long. With Haskell you can use generic datatypes to put into the function and it would automatically work, whether you put in an integer or a double you will get a corresponding result returned. In java you would have to implement either multiple different methods or add generic types to the method. I think having a smaller code base is better for small coding projects or well defined libraries used within a bigger system, but for bigger projects having methods that make clear which data type is returned when is better, because they can't have confusion over what the returned value will be with a certain input. Reasons why this is: ( https://www.destroyallsoftware.com/talks/wat )

Executing function in double.hs has the following result:

Prelude> :load double.hs
[1 of 1] Compiling Main             ( double.hs, interpreted )
Ok, 1 module loaded.
*Main> double 5.0
10.0

Apparently for the typing 'issue' (since it's not really an issue) there is a solution:

double :: Integer -> Integer

This way you can specify the input and output value of the function.

Tuples and lists

Haskell has a data type called "tuple". Tuples are like lists, but they are immutable. This means they have a fixed number of elements. Tuples can also store multiple values with different data types like this:

t = (True, 1, "hello")

I think tuples are a great way of programming in a different way which will make sense for some use cases.

Haskell also uses lists like we touched on before with the character list. There are multiple different ways to define them:

// using head and tail
let h:t = [1, 2, 3]

// without head and tail
1:[2, 3]

[1]:[[2], [3, 4]]

Day one self study

How many different ways can you find to write allEven? I managed to find another way to create an allEven function:

allEven2 :: [Integer] -> [Integer]
allEven2 [] = []
allEven2 nums = [x | x <- nums, even x]

After going through day two I found two other ways to define the allEven function:

allEven3 :: [Integer] -> [Integer]
allEven3 [] = []
allEven3 nums = filter even nums

allEven4 :: [Integer] -> [Integer]
allEven4 [] = []
allEven4 nums = [x | x <- nums, mod x 2 == 0]

Write a function that takes a list and returns the same list in reverse.

reverseList [] = []
reverseList x = reverse x

reverseList2 :: [a] -> [a]
reverseList2 [] = []
reverseList2 (x:xs) = (reverseList2 xs) ++ [x]

Write a function that builds two-tuples with all possible combinations of two of the colors black, white, blue, yellow and red. Note that you should only include one of (black, blue) and (blue, black).

colors = ["black", "white", "blue", "yellow", "red"]
twoTuples = [(a, b) | a <- colors, b <- colors, a /= b]

Write a list comprehension to build a chilhood multiplication table. The table would be a list of three-tuples where the first two are integers from 1-12 and the third is the product of the first two.

input = [1 .. 12]
multiplicationTable = [(x, y, x * y) | x <- input, y <- input]

There are 5 states who can't border with a state with the same color. There are only three colors to be given to the states: Image I started off adding the colors and states to a list:

colors = ["red", "green", "blue"]
states = ["Alabama", "Mississippi", "Georgia", "Tennessee", "Florida"]

But I didn't seem to get the function working using the lists available:

combined = [(x, y) | x <- states, y <- colors]

After that I tried to define the adjecent states in tuples, so that I could try and work with that:

adjacentStates = [
    ("Tennessee", "Mississippi", "Georgia", "Alabama"),
    ("Mississippi", "Tennessee", "Alabama"),
    ("Alabama", "Mississippi", "Georgia", "Tennessee"),
    ("Georgia", "Tennessee", "Alabama", "Florida"),
    ("Florida", "Georgia", "Alabama")
]

This is where I found out that a list of tuples needs the exact same amount of parameters in each tuple. So this list wasn't going to work. Finally I found a way to define the list with states and their allowed colors by defining for each state of which color it can't be the same:

mapColoring = [ ("Tennesee", t, "Mississippi", m, "Alabama", a, "Georgia", g, "Florida", f) |
                  t <- colors, m <- colors, a <- colors, g <- colors, f <- colors,
                  m /= t, m /= a,
                  a /= t, a /= g, a /= f,
                  g /= f, g /= t ]

Although this solution is hardcoded, it does solve the map-coloring problem and looks more like the previous functions I wrote.

Wrapping up

Today I learned a lot about the basics of Haskell. I can barely wait to learn more about this interesting language.

Useful links and more information

https://wiki.haskell.org/Haskell https://www.haskell.org/community https://wiki.haskell.org/User_groups https://downloads.haskell.org/~ghc/8.2.1/docs/html/users_guide/index.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment