Reputation: 475
Which is the alternative implementation in Scala of preprocessor directives, like in C++? Let say that I have something like this:
#ifdef ADD
class Add extends Expr {
Expr left , right ;
Add ( Expr l, Expr r)
{ left =l; right =r; }
#ifdef EVAL
double eval () {
return left.eval () +
right.eval ();
}
#endif
#ifdef PRINT
void print () {
left.print ();
System.out.print("+");
right.print();
}
#endif
}
#endif
How I can have an equivalent of this in Scala?
Upvotes: 1
Views: 763
Reputation: 8227
First, you have to ask yourself, "Why do I want to conditionally include pieces of code that impact many objects?" (Expanding my comment)
Traditionally, preprocessing-controlled code is used for
For the first item, feature configuration control, it is very important to note that using the preprocessor approach, while convenient in the small scale, really tends to obscure the software and can lead you down rabbit holes.
The Scala way is to define a set of interfaces, called traits, and then bring those interfaces together into a complete picture. If you really need to have a particular component not included in the code, then you create a set of classes with only the code you want and package them up into a module (package or jar). Then you include that module in your final packaging.
The second concern is addressed in a similar fashion. You create packages for common code, and packages for the platform specific items. Then, at run time, you dynamically select which packages you load. This can be done by explicit package paths, or if you want to be tricky about it, you might try creating a class loader to do the dirty work for you. However, being up front about things works much better in the long term.
The third concern is addressed by several approaches in Scala.
An approach used in Java and in Scala is annotations. These use a programmatic flagging method to indicate that particular classes, methods or fields have special treatment. See the Scala documentation. However, using it to flag methods as not being present is not a good use, even though it is possible. The @deprecated
annotation, for instance, is used to tell programmers when API elements are going away, and allows the compiler or IDE to flag items that should be upgraded.
Another approach available in Scala is the use of macros. From this page, we have the blurb: Macros are good for code generation, static checks and domain-specific languages. In theory, you could use them for what you have been trying to do with pre-processing.
In general, the pre-processing approach leads to "kitchen sink" type programming. The other issue that is a huge pain to deal with is that your method requires that every single module in the application, whether library or main program, needs to be compiled with the same flags. This is a tremendous burden on your make system, especially once you pass the hundreds of source files mark.
Upvotes: 1
Reputation: 221
The C/C++ preprocessor will not emit anything for the code in the ifdef blocks if the corresponding constants are not defined. Scala does not have a preprocessor and as far as I know the only way to exclude Scala code from byte code generation is to exclude the code from the build.
You could do that with sbt by using the logic to define ADD to control whether Add.scala is part of the build.
The EVAL and PRINT constants would have to be handled similarly, with a file for each and making the methods available on the Add class using the same tricks that are used to add methods to e.g. Double or Long. I don't really know if it is possible to combine RichC1 and RichC2 into a single class, that would be an interesting question on its own.
In any case this would cough almost certainly result in an enormous mess cough. And the only benefit is an infinitesimal reduction in code size. If this reduction matters it is unlikely that Scala is the right tool in the first place. You might be better off by simply deleting the ifdefs and including all of the code in the build.
Upvotes: 2