1

I'm new to haskell. I read this answer and I would also like to test the type of my functions, if there is a way to do it. Here is an example : test.hs

module Test where
import Test.HUnit

test1 = TestCase (assertEqual "test1" 3 (length [1,2,3]))
tests = TestList [test1]

main :: IO Counts
main = runTestTT tests 

I'm running the code with runghc test.hs and I get:

Cases: 1  Tried: 1  Errors: 0  Failures: 0

Now, how can I test the type for the length function ?

I have already tried adding a second test test2 = TestCase (assertEqual "test2" "length :: Foldable t => t a -> Int" :type length), but I get this error test.hs:5:77: parse error on input 'type'.

Thanks

Community
  • 1
  • 1
Ionut
  • 1,729
  • 4
  • 23
  • 50

2 Answers2

9

Writing a test for a type is almost definitely not what you want. Since Haskell is statically typed, the compiler automatically checks types for you when you compile, with no need to run any code or tests. If length did not have a compatible type, you would get a type error if you tried to load the code at all.

:type is a special command from ghci, not actual Haskell syntax. You can't use it directly in a normal program. There are ways to get a value representing a type but they're somewhat complicated and, again, almost definitely not what you need.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • Thanks for the explanation. – Ionut Sep 28 '15 at 18:53
  • 3
    Just to play devil's advocate: I could imagine wanting something like this for a library that had e.g. a forwards-compatible interface and an "internal" interface; then you might want tests that check that no hapless developer inadvertently changes the type of some part of the forwards-compatible interface. (The library interface could certainly contain operations that are useful to library consumers, but not actually called anywhere by the library itself, hence would not cause compile failure if their types changed.) – Daniel Wagner Sep 28 '15 at 21:54
3

Although I agree with the other answer that the question smells, I think it is worth giving a direct answer anyway. You could put a type assertion in your test suite like this:

module Test where

check_type_of_length :: Foldable t => t a -> Int
check_type_of_length = length

If length can not be given the type you requested, the module will not compile. However, this is not a perfect test: if the type of length is actually more polymorphic than the type you demand, this will still "pass"; thus, for example, the following module still compiles, even though undefined does not have exactly the type Foldable t => t a -> Int.

module Test where

check_type_of_length :: Foldable t => t a -> Int
check_type_of_length = undefined

We can make the test a bit more precise if you want to check a monomorphic type. The idea is to use Typeable, as suggested by @dfeuer. That would look like this:

module Test where

import Data.Typeable

test_type_of_not :: Bool
test_type_of_not = typeOf not == typeOf (undefined :: Bool -> Bool)

If not has the monomorphic type Bool -> Bool, then test_type_of_not will be True; if it has a different monomorphic type, it will be False; and if not suddenly becomes polymorphic, the module will fail to compile with an ambiguity error.

I do not know an analogous way to test that a polymorphic type does not become more polymorphic; you could perhaps do something nasty like running a short bash script that does

echo :t length | ghci

and checking its output, or something more direct with the GHC API. However, an approach like this is likely to be very fragile.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 2
    The most direct way by far (much easier than running GHCi from your program) is to use `typeRep :: forall proxy a. Typeable a => proxy a -> TypeRep`. You can actually print those out. – dfeuer Sep 28 '15 at 19:13
  • 1
    Urk... I guess `Typeable` probably has issues dealing with polymorphic things. *sigh* – dfeuer Sep 28 '15 at 20:45
  • @dfeuer Thanks for the suggestion. I've included some notes about `Typeable`. – Daniel Wagner Sep 29 '15 at 02:31