Reputation: 2741
I'm writing a module with Haskell and I would like to create two versions of this module :
For convenience, I would like these two modules (with two differents names) to share the same source files. the two modules will have the same type and same functions with some differences ('extended' version will rely on the 'basic' one).
My idea is to have something like this :
module MyLib where -- for 'basic' version
module MyLibExt where -- for 'extended' version
MyType =
TypeA -- for 'basic' version
| TypeB -- for 'basic' version
| TypeC -- for 'basic' version
| TypeExtendedD -- for 'extended' version
| TypeExtendedE -- for 'extended' version
MyFunction TypeA = ... -- for 'basic' version
MyFunction TypeB = ... -- for 'basic' version
MyFunction TypeExtendedD = ... -- for 'extended' version
and build the two modules with some compilation directive given to GHC/Cabal.
Is it possible to do a such thing with Haskell ?
What are the GHC/Cabal compilation directive which can be useful to make conditionnal building ?
Upvotes: 3
Views: 77
Reputation: 60513
Consider abstracting. It may not be worth it, but sometimes there is beauty and power hiding, and it can only be teased out with abstraction. For your case MyType
and MyFunction
, perhaps it goes like this (just an example, and it could look very different depending on your details):
class MyFunction a where
myFunction :: a -> Int
data MyType = TypeA | TypeB | TypeC
instance MyFunction MyType where
myFunction TypeA = 0
myFunction TypeB = 1
myFunction TypeC = 2
data MyTypeExt = Orig MyType | TypeExtendedD | TypeExtendedE
instance MyFunction MyTypeExt where
myFunction (Orig x) = myFunction x
myFunction TypeExtendedD = myFunction TypeC + 1
myFunction TypeExtendedE = myFunction TypeC + 2
That would be the first level. You can go further, making the extension parametric:
data MyTypeExt a = Orig a | TypeExtendedD | TypeExtendedE
instance (MyFunction a) => MyFunction (MyTypeExt a) where
... -- defined so that it works for any myFunction-able Orig
Then it's easy to separate out into different files. But the advantages of abstracting go far beyond splitting things into public and private spaces. If there are two "valid" versions of your code, then those two versions each have consistent meanings. Find what they have in common, see if you can code what they have in common without referencing either one in particular. Then look carefully -- what else has these things in common with your two examples? Can you build your two examples out of yet simpler pieces that also have those things in common? In Haskell, code programs you.
Upvotes: 0
Reputation: 584
You can't put two modules in the same file. But you can sort of get what you want with a bit of re-exporting.
One file would have both the basic and extended code (I shortened it a bit):
module MyLibExt where
MyType = TypeA | TypeB | TypeC | TypeExtendedD | TypeExtendedE
myFunction TypeA = ...
myFunction TypeB = ...
myFunction TypeExtendedD = ...
then the other file would be the basic one:
module MyLib (MyType (TypeA, TypeB, TypeC), myFunction)
import MyLibExt
This way if someone imports just MyLib
they only get access to the basic constructors, but not the extended ones. myFunction
will still work on TypeExtendedD
values like it would in MyLibExt
, but since we're unable to create those values with just MyLib
, that's fine.
More generally, when you define your module, you can say what exactly you want to export. Here are some basic examples:
module Example (
exampleFunction, -- simply export this function.
ExampleType1 (), -- export the type, but no constructors.
ExampleType2 (ExampleConstructor1, ExampleConstructor2), -- export the given type and its given constructors. You can't export constructors like functions, you have to do it like this).
ExampleType3 (..), -- export given type and all its constructors.
ExampleClass1, -- export type class, but no functions that belong to it.
ExampleClass2 (exampleFunction2, exampleFunction3), -- export type class and the given functions belonging to it. You can also export these functions as though they were normal functions, like in the first of the examples.
ExampleClass3 (..), -- export type class and all its functions.
module Module1, -- re-export anything that is imported from Module1.
) where
You can export anything that is in scope, including anything that you imported from other modules. In fact if you want to re-export something from other modules, you need to explicitly define an export list like this, by default it will only export whatever is defined in this module.
Upvotes: 1