I'm writing a snake game in Haskell. These are some of the things I have:
Coord
data typeLine
data typeRect
data typePolygon
type class, which allows me to get a Rect
as a series of lines ([Line]
).Impassable
type class that allows me to get a Line
as a series of Coords ([Coord]
) so that I can detect collisions between other Impassable
s.Draw
type class for anything that I want to draw to the screen (HSCurses).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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With