mfrachet
mfrachet

Reputation: 8932

Learning Haskell, understanding data structures

I m actually learning Haskell and I'm trying to make a simple FizzBuzz Kata.

The idea is to take a list of numbers as entry, and to produce a list of Numbers | String following these rules:

Here the code I've produced:

module FizzBuzz where 

data StringInt a = Int a | String a

handleRule:: StringInt a -> a
handleRule x
    | x % 3 == 0 = StringInt "Fizz"
    | x % 5 == 0 = StringInt "Buzz"
    | otherwise = x

run:: [StringInt a] -> [a]
run = map handleRule

While trying to run the following tests:

module FizzBuzzSpec (spec) where

    import Test.Hspec
    import FizzBuzz

    spec :: Spec
    spec = describe "FizzBuzz#run" $ do
        it "should have displayed Fizz for number 3" $
            run [1..10]:2 `shouldBe` "Fizz"
        it "should have displayed Buzz for number 5" $
            run [1..10]:4 `shouldBe` "Buzz"

I'm having the following outputs:

• Occurs check: cannot construct the infinite type: a ~ StringInt a
• In the expression: x
  In an equation for ‘handleRule’:
      handleRule x
        | (%) x 3 == 0 = StringInt "Fizz"
        | (%) x 5 == 0 = StringInt "Buzz"
        | otherwise = x
• Relevant bindings include
    x :: StringInt a (bound at src/FizzBuzz.hs:6:12)
    handleRule :: StringInt a -> a (bound at src/FizzBuzz.hs:6:1)

| 9 | | otherwise = x

Important Note: I'm really a new Haskell player, sorry if I'm making horrible things right there.

Do you have an idea what I'm doing wrong ?


EDIT:

I've tried by using:

module FizzBuzz where 

type StringInt = Either Int String

handleRule:: Int -> StringInt
handleRule x
    | x `mod` 3 == 0 = Right "Fizz"
    | x `mod` 5 == 0 = Right "Buzz"
    | otherwise      = Left x

run:: [Int] -> [StringInt]
run = map handleRule

And

module FizzBuzzSpec (spec) where

    import Test.Hspec
    import FizzBuzz

    spec::Spec
    spec = describe "FizzBuzz#run" $ do
        it "should have displayed Fizz for number 3" $
            run [1..10]:2 `shouldBe` Right "Fizz"
        it "should have displayed Buzz for number 5" $
            run [1..10]:4 `shouldBe` Right "Buzz"

But that always throws

    • Couldn't match expected type ‘[[StringInt]]’
                  with actual type ‘Either a0 [Char]’
    • In the second argument of ‘shouldBe’, namely ‘Right "Fizz"’
      In the second argument of ‘($)’, namely
        ‘run [1 .. 10] : 2 `shouldBe` Right "Fizz"’
      In a stmt of a 'do' block:
        it "should have displayed Fizz for number 3"
          $ run [1 .. 10] : 2 `shouldBe` Right "Fizz"
  |
9 |                         run [1..10]:2 `shouldBe` Right "Fizz"
  |                                                  ^^^^^^^^^^^^

/Users/pc/Soft/haskell/hello-stack/test/FizzBuzzSpec.hs:11:50: error:
    • Couldn't match expected type ‘[[StringInt]]’
                  with actual type ‘Either a1 [Char]’
    • In the second argument of ‘shouldBe’, namely ‘Right "Buzz"’
      In the second argument of ‘($)’, namely
        ‘run [1 .. 10] : 4 `shouldBe` Right "Buzz"’
      In a stmt of a 'do' block:
        it "should have displayed Buzz for number 5"
          $ run [1 .. 10] : 4 `shouldBe` Right "Buzz"
   |
11 |                         run [1..10]:4 `shouldBe` Right "Buzz"
   |                              

                ^^^^^^^^^^^^

Can't get the point out of here...

Thanks for your help guys

Upvotes: 0

Views: 91

Answers (2)

Igor Drozdov
Igor Drozdov

Reputation: 15055

I assume, that by the following line:

data StringInt a = Int a | String a

you're going to define a sum type. But actually, it requires data constructor provided:

data StringInt = Left Int | Right String

Or, there's already Either type, which helps to define a sum type, so you can just create a type alias. Thus, your program is going to look like this

type StringInt = Either Int String

handleRule:: Int -> StringInt
handleRule x
    | mod x 3 == 0 = Right "Fizz"
    | mod x 5 == 0 = Right "Buzz"
    | otherwise = Left x

run:: [Int] -> [StringInt]
run = map handleRule

And the test should also be modified, since the return value is not just a string, but a string wrapped in Right:

module FizzBuzzSpec (spec) where

import Test.Hspec
import FizzBuzz

spec::Spec
spec = describe "FizzBuzz#run" $ do
    it "should have displayed Fizz for number 3" $
        run [1..10] !! 2 `shouldBe` (Right "Fizz")
    it "should have displayed Buzz for number 5" $
        run [1..10] !! 4 `shouldBe` (Right "Buzz")

main :: IO()
main = hspec spec

To run the tests:

runhaskell Tests.hs

By the way, you can skip the sum types for now, just by returning a string representation of a number if it can't be divided by 3 or 5:

handleRule:: Int -> String
handleRule x
    | mod x 3 == 0 = "Fizz"
    | mod x 5 == 0 = "Buzz"
    | otherwise = show x

run:: [Int] -> [String]
run = map handleRule

Upvotes: 1

Petras Purlys
Petras Purlys

Reputation: 1155

The rule should be something like this:

data StringInt = SI_Str String
               | SI_Int Int

handleRule:: Int -> StringInt
handleRule x
    | x `mod` 3 == 0 = SI_Str "Fizz"
    | x `mod` 5 == 0 = SI_Str "Buzz"
    | otherwise      = SI_Int x

When you describe a "data", you're actually describing a data type. The left sife of the equation is the name of the type and in this case the name of the type is StringInt. On the right side of the equation you describe the constructor (or constructor, as is in this case) how you can create the data type. In this case, we have 2 constructors the can create "datas" of type StringInt - the SI_Str constructor and the SI_Int constructor. How you distinguish between them and access their "data contents" is called pattern-matching. Trying not to spoil the fun of learning though, I suggest starting carrying on from there.

Upvotes: 2

Related Questions