Reputation: 8932
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
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
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