How can you be DRY with a programming language tha

2019-03-30 05:21发布

问题:

Any programming language that does not have a suitable reflection mechanism I find seriously debilitating for rapidly changing problems.

It seems with certain languages its incredible hard or not possible to do:

  • Convention over Configuration
  • Automatic Databinding
  • AOP / Meta programming

with out reflection.

Some example languages that do not have some sort of programmatic reflection are: C, C++, Haskell, OCaml. I'm sure there are plenty more.

To show you can example of DRY (Don't Repeat Yourself) being violated by most of these languages is when you have to write Unit Tests. You almost always need to register your test cases in these languages outside of where you define the test.

How do programmers of these languages mitigate this problem?

EDIT: Common languages that do have reflection for those that do not know are: C#, Java, Python, Ruby, and my personal favorite F# and Scala.

EDIT: The two common approaches it seems are code instrumentation and code generation. However I have never seen instrumentation for C.

Instead of just voting to close, could some one please comment on why this should be closed and I'll delete the post.

回答1:

You don't.
But you can keep the repetitions close to each other so when changing something, you see something else has to be changed too.

For example, I wrote a JSON-Parser that outputs objects, a typical call looks like this:

struct SomeStruct
{
        int a;
        int b;
        double c;

        typedef int serializable;
        template<class SerializerT> void serialize(SerializerT& s)
        {
                s("a",a)("b",b)("c",c);
        }
};

Sure, when you add a field, you have to add another field in the function, but maybe you don't want to serialize that field (something you'd have to handle in languages with reflection, too), and if you delete a field without removing it from the function, the compiler will complain.



回答2:

I think it's a matter of degree. Reflection is just one very powerful method of avoiding repetition.

Any time you generalize a function from a specific case you are using DRY principle, the more general you make it the more DRY it is. Just because some languages don't get you where you get with reflection doesn't mean there aren't DRY ways of programming with them. They may not be as DRY, but that doesn't mean they don't have their own unique advantages which in total sum may outweigh the advantages of using a language that has reflection. (For example, speed consequences from heavy use of reflection could be a consideration.)

Also, one method of getting something like the DRY benefits of reflection with a language that doesn't support it is by using a good code-generation tool. In that case you modify the code for different cases once, in the code generation template, and the template pushes it out to different instances in code. (I'm not saying whether or not using code generation is a good thing, but with a good "active" generator it is certainly one way of getting something like the DRY benefit of reflection in a language that doesn't have reflection. And the benefits of code generation go beyond this simple benefit. I'm thinking of something like CodeSmith, although there are many others: http://www.codesmithtools.com/ )



回答3:

  1. Abstractly, do more at runtime, without the benefits of things like compile-time type checking (you have to essentially write your own type-checking routines) and beautiful code. E.g., use a table instead of a class. (But if you did this, why not use a dynamically-typed language instead?) This is often bad. I do not recommend this.

  2. In C++, generic programming techniques allow you to programmatically include members of a class (is that what you want to do?) via inheritance.



回答4:

One nice example for C++ unit testing is cxxtest: http://cxxtest.tigris.org/. It uses convention and a python script to generate your C++ test suite by post-processing your C++ with python.

A good way to think about getting around restrictions in languages is Michael Feathers' notion of "seams". A seam is a place where your program can be changed without changing the code. For example, in C the pre-processor and linker provide seams. In C++ polymorphism is another place. In more dynamic languages like where you can change method definitions, or reflect, you get even more flexibility. Without the seams things can be more complicated and sometimes you just don't want to try to hammer a nail with your shoe but rather go with the flow of the tool at hand.