hspec - type issue in error assertion

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

hspec - type issue in error assertion

Paweł Bałaga
Hello fellow Haskellers!

I'm approaching learning the basics of Haskell by going through https://wiki.haskell.org/99_questions. At the same time I write tests for my code in Hspec. Consider question no. 2: "Find the last but one element of a list".

My solution:
-- Problem 02
myButLast :: [a] -> a
myButLast [] = error "Empty list"
myButLast [x] = error "List has only one element"
myButLast [x1,x2] = x1
myButLast (x:xs) = myButLast xs

and a  a test:
describe "02 myButLast" $ do
it "returns the last but one element of a list" $ do
myButLast [] `shouldThrow` anyErrorCall
myButLast [1] `shouldThrow` anyErrorCall -- <- this line causes the problem
myButLast [1..4] `shouldBe` 3
myButLast ['x','y','z'] `shouldBe` 'y'
myButLast "abc" `shouldBe` 'b'


Building tests with stack test command causes the below compilation error:

    • No instance for (Num (IO a0)) arising from the literal ‘1’
    • In the expression: 1
      In the first argument of ‘myButLast’, namely ‘[1]’
      In the first argument of ‘shouldThrow’, namely ‘myButLast [1]’
   |
27 |             myButLast [1] `shouldThrow` anyErrorCall
   | 


From what I understand, type of myButLast [1] is different than expected by shouldThrow. What I don't understand is why exactly it behaves so and how to fix this problem. Only that one assertion doesn't compile. The others are fine. Particularly, why does myButLast [] `shouldThrow` anyErrorCall  work but with one element it doesn't?

Can you please give me a hand?


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: hspec - type issue in error assertion

Yuji Yamamoto
According to my guess:
  1. shouldThrow receives `IO a` as a first argument.
  2. So, both ` myButLast [] ` and ` myButLast [1] ` must be some `IO a`
  3. Then,  ` myButLast` 's type is inferred as `[IO a] -> IO a` when given `[]`: the element of the list is `IO a` (Which actually would be `IO ()` by the default rule).
  4. Similarly,  ` myButLast [1] ` 's `a` (the type of the element of the list) must be `IO something`. But any of `IO something` can't be a `Num`'s instance (as the error message " No instance for (Num (IO a0)) arising from the literal ‘1’ " says).
That's why  ` myButLast [1] ` can't be compiled.

To fix it, use `evaluate`:
`evaluate  myButLast [1]`

One more note: maybe you know we usually should not use `error`, which raises an error in a pure function.
So, we usually handle errors in an `IO` context. That's why `shouldThrow` receives an `IO a`.



2018年7月9日(月) 2:57 Paweł Bałaga <[hidden email]>:
Hello fellow Haskellers!

I'm approaching learning the basics of Haskell by going through https://wiki.haskell.org/99_questions. At the same time I write tests for my code in Hspec. Consider question no. 2: "Find the last but one element of a list".

My solution:
-- Problem 02
myButLast :: [a] -> a
myButLast [] = error "Empty list"
myButLast [x] = error "List has only one element"
myButLast [x1,x2] = x1
myButLast (x:xs) = myButLast xs

and a  a test:
describe "02 myButLast" $ do
it "returns the last but one element of a list" $ do
myButLast [] `shouldThrow` anyErrorCall
myButLast [1] `shouldThrow` anyErrorCall -- <- this line causes the problem
myButLast [1..4] `shouldBe` 3
myButLast ['x','y','z'] `shouldBe` 'y'
myButLast "abc" `shouldBe` 'b'


Building tests with stack test command causes the below compilation error:

    • No instance for (Num (IO a0)) arising from the literal ‘1’
    • In the expression: 1
      In the first argument of ‘myButLast’, namely ‘[1]’
      In the first argument of ‘shouldThrow’, namely ‘myButLast [1]’
   |
27 |             myButLast [1] `shouldThrow` anyErrorCall
   | 


From what I understand, type of myButLast [1] is different than expected by shouldThrow. What I don't understand is why exactly it behaves so and how to fix this problem. Only that one assertion doesn't compile. The others are fine. Particularly, why does myButLast [] `shouldThrow` anyErrorCall  work but with one element it doesn't?

Can you please give me a hand?

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


--

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.