Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How small should I make make modules in Haskell?

I'm writing a snake game in Haskell. These are some of the things I have:

  • A Coord data type
  • A Line data type
  • A Rect data type
  • A Polygon type class, which allows me to get a Rect as a series of lines ([Line]).
  • An Impassable type class that allows me to get a Line as a series of Coords ([Coord]) so that I can detect collisions between other Impassables.
  • A Draw type class for anything that I want to draw to the screen (HSCurses).
  • Finally I'm using QuickCheck so I want to declare Arbitrary instances for a lot of these things.

Currently I have a lot of these in separate modules so I have lots of small modules. I've noticed that I have to import a lot of them for each other so I'm kind of wondering what the point was.

I'm particularly confused about Arbitrary instances. When using -Wall I get warnings about orphaned instances when I but those instances together in one test file, my understanding is that I can avoid that warning by putting those instances in the same module as where the data type is defined but then I'll need to import Test.QuickCheck for all those modules which seems silly because QuickCheck should only be required when building the test executable.

Any advice on the specific problem with QuickCheck would be appreciated as would guidance on the more general problem of how/where programs should be divided into modules.

like image 867
Ollie Saunders Avatar asked Oct 23 '10 00:10

Ollie Saunders


3 Answers

You can have your cake and eat it too. You can re-export modules.

module Geometry 
    ( module Coord, module Line, module Rect, module Polygon, module Impassable )
where

I usually use a module whenever I have a complete abstraction -- i.e. when a data type's meaning differs from its implementation. Knowing little about your code, I would probably group Polygon and Impassable together, perhaps making a Collision data type to represent what they return. But Coord, Line, and Rect seem like good abstractions and they probably deserve their own modules.

like image 195
luqui Avatar answered Oct 31 '22 21:10

luqui


For testing purposes, I use separate modules for the Arbitrary instances. Although I generally avoid orphan instances, these modules only get built when building the test executable so I don't mind the orphans or that it's not -Wall clean. You can also use -fno-warn-orphans to disable just this warning message.

like image 32
John L Avatar answered Oct 31 '22 22:10

John L


I generally put more emphasis on the module interface as defined by the functions it exposes rather than the data types it exposes. Do some of your types share a common set of functions? Then I would put them in the same module.

But my practise is probably not the best since I usually write small programs. I would advise looking at some code from Hackage to see what package maintainers do.

If there were a way to sort packages by community rating or number of downloads, that would be a good place to start. (I thought there was, but now that I look for it, I can't find it.) Failing that, look at packages that you already use.

like image 1
Nathan Shively-Sanders Avatar answered Oct 31 '22 22:10

Nathan Shively-Sanders