0

Is there any statically-typed, strongly-type compiled language that provides a functionality to iterate over a type's members at compile time and generate templated code for each one? For example, it could be something like:

// in pseudo-C#

public static void AddParameter(string parameterName, object value) { /* ... */ }

public static void AddParameters<T>(T parameters) {
    // Of course, the memberof(T), membersof(T), membername(<member>)
    // and membervalue(<member>, object) operators would be valid
    // inside a "compile for" block only
    compile for (memberof(T) member in membersof(T))
        AddParameter(membername(member), membervalue(member, parameters));

    /* If this were actual C#, the "compile for" block could even have a where clause */
}

So, if the following call was made:

StaticClass.AddParameters(new { UserID = "eleon", Password = "Gu3$$17" });

Then that particular instantiation of AddParameters would be unrolled to

public static void AddParameters(InternalNameOfTheAnonymousType parameters) {
    AddParameters("UserID",   parameters.UserID);
    AddParameters("Password", parameters.Password);
}

At compile-time (if it were actual C# at IL-to-native compile time)

isekaijin
  • 19,076
  • 18
  • 85
  • 153
  • Why do you need to iterate over all the members? – Marcin Jun 27 '11 at 22:47
  • _In theory_, you can use T4 to do that in C#. In practice, that would be very difficult to manage. – configurator Jun 27 '11 at 22:48
  • @Marcin: Because I am too lazy to type everything, yet I don't want the overhead of Reflection. Even worse, in a language like C++ I simply would not be able to do that, unless I used template specialization, which would defeat the purpose of the templating. – isekaijin Jun 27 '11 at 22:49
  • Why do you need to generate templated code? Why not just write a single template? – Marcin Jun 27 '11 at 22:53
  • @Marcin: Because there is no way to express what I want to say using a single template. What I want to say is generic in a way that neither generics nor templates contemplate. – isekaijin Jun 27 '11 at 22:59
  • I'm pretty sure there is. If your template can be generated from a run of the iteration, why can't it also be generated by just calling the template on a member of the type in question when actually encountered in your normal code? Or, even more pertinently, why use templating at all for this? This seems like a premature optimisation. – Marcin Jun 27 '11 at 23:02
  • 1
    @Marcin: I wouldn't call something that reduces the amount of code I have to write by some 15% (and that is a conservative estimate) a "premature optimization". – isekaijin Jun 27 '11 at 23:08
  • It's really not clear to me what you think this would achieve, on any level. What are you doing now that you want to replace with this approach? – Marcin Jun 27 '11 at 23:11
  • 1
    @Marcin: I am currently using Reflection inside the generic method `AddParameters`, but, since Reflection is slow, I have to maintain silly static `Dictionary`s to avoid calling `Type.GetProperties()` several times for the same type. – isekaijin Jun 27 '11 at 23:16
  • What are you using reflection for? You still haven't said what you are actually trying to accomplish. – Marcin Jun 27 '11 at 23:18
  • @Marcin: I want to pass each property of an anonymous type as a parameter to some request object. – isekaijin Jun 27 '11 at 23:20
  • This seems fundamentally misconceived. Why not just not use an anonymous type? Either, nail down the possible parameters, or use objects that know about themselves, and can do this, or just use a hashtable. – Marcin Jun 27 '11 at 23:28
  • @Marcin: A hashtable would be as inefficient as using Reflection, only in a different way. And, actually, there is no limited set of possible parameters. – isekaijin Jun 27 '11 at 23:29
  • 1
    How is a hashtable inefficient? – Marcin Jun 27 '11 at 23:41
  • @Marcin: An anonymous object can be allocated in a single memory block of just as much memory as it needs. A hashtable wastes space. – isekaijin Jun 28 '11 at 00:06
  • 1
    @Eduardo Leon: So use a different type of dictionary structure, like an alist. – Marcin Jun 28 '11 at 00:09
  • 1
    T4 is [Text Template Transformation Toolkit](http://msdn.microsoft.com/en-us/library/bb126445.aspx) – configurator Jun 28 '11 at 03:39
  • @Marcin, read more about metaprogramming. There's no alternative to a code generation - any runtime technique would be much slower and much less elegant than a decent code generation. Your hashtable is a runtime solution, whereas code generation takes all the burden into a compilation time. – SK-logic Jun 28 '11 at 15:59
  • @SK-logic: (a) you've just admitted that runtime logic is an alternative to code generation (b) code generation can only use the information at code-generation-time. This is probably not what is wanted for extracting key-value pairs (c) code generation is only free if you have all the information necessary to pick exactly the right piece of code at compile time. – Marcin Jun 28 '11 at 17:06
  • @Marcin: I _do_ have all the information that is needed to _generate_ (not pick) the right piece of code at JIT-compile time, when generics are instantiated. – isekaijin Jun 28 '11 at 17:08
  • @Eduardo: Right, so your code will probably run as quickly, or very slightly quicker, than using an appropriate keyed data structure, at the potential cost of using non-standard generation technologies, with all the maintenance issues that imports. Unless profiling has shown you need this optimisation, that seems like a poor choice. – Marcin Jun 28 '11 at 17:13
  • @Marcin: Non-standard? You got it right on the money! That was why I was asking whether there was a language that made it a standard. – isekaijin Jun 28 '11 at 17:25
  • @Marcin, I'm quite serious. And, static type information is not mandatory - in Lisp we've got almost no type system, but the power of metaprogramming available is still unmatched by anything else. And compilation time code generation is not just an optimisation technique - it is an extremely powerful abstraction tool. You simply can't have that level of abstraction if you've got only runtime interpretation. As an exercise - try to build another language on top of your main one, make it embeddable and seamlessly integrated. Say, implement a Datalog or ML. – SK-logic Jun 28 '11 at 18:25
  • @SK-logic: Why would I want to have it embedded and seamlessly integrated? Just because powerful metaprogramming is required to achieve it, it doesn't mean it's a good idea. Eduardo's request is another example of this. – Marcin Jun 28 '11 at 18:34
  • @Marcin, you would want to embed a language with a different semantics if you have to solve a problem which is most naturally expressed in terms of that semantics. And *all* the problems in fact have their own semantics. Read about http://en.wikipedia.org/wiki/Domain_specific_language and http://en.wikipedia.org/wiki/Language-oriented_programming – SK-logic Jun 28 '11 at 18:44
  • @Marcin, or, in probably simpler terms: you may want to do it because it will reduce the amount of code in an order of magnitude, and equally increase the readability and maintainability. Still "not a good idea"? – SK-logic Jun 28 '11 at 18:46
  • @SK-logic: Frankly, no. DSLs are touted as a great idea, but in the end turn out to be a maintenance nightmare, especially after the creator has left the organisation. Much better is just to keep different languages in different files. If metaprogramming makes implementing an already-specified language easier, and that's easier than just linking the output from another compiler (or however you want to do it), great. Use it for that. – Marcin Jun 28 '11 at 19:03
  • @Marcin, my experience with DSLs is totally opposite to yours. DSLs are extremely easy to maintain. Even the most dramatic changes in the architecture are easily accommodated by simply rewriting the DSL implementation and keeping all the business logic intact. And it does not matter if you mix languages or keep them in separate files - metaprogramming is still the best way to implement compilers for those languages. You can combine existing language components into new languages, and this way each new DSL definition is tiny (literally, 50-100 LOC maximum) and very maintainable. – SK-logic Jun 28 '11 at 19:50
  • @SK-logic: I'd rather hear from colleagues who are left maintaining your code. – Marcin Jun 28 '11 at 19:52
  • @Marcin, this approach is a mainstream in the Lisp world. We've used it for half a century. Take a look at, say, http://racket-lang.org/- the whole system is implemented this way. New languages are easily added, there is a large community around, and absolutely no maintenance problems. – SK-logic Jun 28 '11 at 19:56
  • @Marcin, see this: http://stackoverflow.com/questions/4548768/collection-of-great-applications-and-programs-using-macros – SK-logic Jun 28 '11 at 19:59
  • @SK-logic: You know that extensive DSLs are controversial in the lisp world. – Marcin Jun 28 '11 at 20:01
  • @Marcin, let's exclude schemers with their hygienic religion. And the combined experience of the rest of us clearly shows that none of the problems you've been talking about actually exist. Compiled macro-based DSLs are very easy to implement, easy to debug and easy to maintain. Please, take a look at the examples, even a single positive example easily beats all the useless philosophy. – SK-logic Jun 28 '11 at 20:04
  • @SK-logic: Not every common lisp programmer is meta-programming their way into a completely different language. There's a difference between judiciously chosen macros and a DSL. After all, there's a reason the LOOP feature is still controversial 20 years after standardisation. – Marcin Jun 28 '11 at 20:10
  • @Marcin, it is a pointless conversation without real examples. I showed mine. What are yours? Please, point me to the examples which will demonstrate an unfixable mess made by clean and idiomatic use of macro metaprogramming. – SK-logic Jun 28 '11 at 20:39
  • @SK-logic: Actually, all you've shown is that some people use macros sometimes. I don't have examples of bad uses of metaprogramming, because I avoid it. – Marcin Jun 28 '11 at 20:41
  • @Marcin, I've given you an example of a large system built almost entirely on metaprogramming. And no puppy chocked to death during its development. Is not it a proof for my point? It it were that bad as you're trying to convince me, they'd never implement it. – SK-logic Jun 29 '11 at 05:54
  • @SK-logic: So, your argument is that no-one (or no programmer) makes bad choices when developing libraries? Is your position that MFC is an excellent library, and all other publicly released libraries are elegant and maintainable? – Marcin Jun 29 '11 at 08:17
  • @SK-logic: In any case, it's not really a counter argument to my general proposition that DSLs are not a great idea. What it shows is that one can implement a new language using metaprogramming. Like any project with a bunch of maintainers, and documentation, it's better than writing your own DSL, which is the point that I made above. It doesn't show that you should be doing this in your own projects. – Marcin Jun 29 '11 at 08:19
  • @Marcin, as I said before - it is pointless. You're apparently religious. People like you won't listen to any proper arguments. You've already decided what is your position. – SK-logic Jun 29 '11 at 09:05
  • @SK-logic: I will listen to *proper* arguments. – Marcin Jun 29 '11 at 14:22
  • @Marcin, even a *single* example of a maintainable system built with metaprogramming is already a *proper* argument. But not for you. You believe that metaprogramming results in an unmaintainable mess, and there is no way you'd ever thing otherwise. – SK-logic Jun 29 '11 at 14:25
  • @SK-logic: You are attacking a man of straw. I am not suggesting that one cannot make a new system based on metaprogramming, but rather that (a) creating embedded DSLs is a poor idea (b) metaprogramming in general has a cognitive load when mixed with "ordinary" code which needs to be justified. Racket does not address either of these issues, because it is its own programming environment. – Marcin Jun 29 '11 at 16:19
  • @Marcin, you've downvoted this question because OP had been looking specifically for a system with "its own programming environment". If a language is designed for this sort of use, is it still a "poor idea"? – SK-logic Jun 29 '11 at 16:27
  • @SK-logic: If a language has gotos, does that make them a good idea? – Marcin Jun 29 '11 at 18:27
  • @Marcin, you're so full of religious prejudice! How can you program with such a burden? Go-tos are fantastic, of course, when you know how to use them properly. Try to implement an efficient bytecode VM without computed gotos (see OCaml for reference). Try to implement efficiently a state machine (see Knuth's Adventure program). Try to generate an intrinsically state machine code if your target language does not provide goto. You'd be much better off if you forget all the religious crap you believe in and start learning programming from scratch. I'd recommend SICP for beginning. – SK-logic Jun 29 '11 at 18:36
  • @SK-logic: Are you trying to unlock a cheat mode in SO by saying "religious" an unseemly number of times? Your examples for the use of goto make a great analogy for the times when embedded DSLs are a good idea: extremely limited circumstances, in order to express something that cannot be well expressed in another way, generally for use by a large number of other programmers, thus amortising the maintenance costs. – Marcin Jun 29 '11 at 18:47
  • @Marcin let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/985/discussion-between-sk-logic-and-marcin) – SK-logic Jun 29 '11 at 19:08

2 Answers2

4

You can do it with Nemerle.

SK-logic
  • 9,605
  • 1
  • 23
  • 35
  • Very nice! Does Nemerle need a particular runtime besides the .NET runtime? If not, I'll start using it right now. – isekaijin Jun 28 '11 at 16:37
  • 1
    @Eduardo León, it does not add any dependencies. Also you can use an almost complete C# as your source language, using Nemerle-specific features only where needed. – SK-logic Jun 28 '11 at 16:40
3

The syntactic briars are thick here, so it's hard for me to see what you're getting at, but I think Haskell's Scrap Your Boilerplate might be powerful enough to do the trick. It certainly is capable of some amazing compile-time generic metaprogramming.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533