Fizzbuzz in SNOBOL4

Posted by Daniel Lyons Mon, 31 Dec 2007 18:06:00 GMT


**  FizzBuzz in SNOBOL4
**
**  Daniel Lyons <fusion@storytotell.org>
**
**  Yes, I am insane, before you ask.
**
**

        I = 1
LOOP    FIZZBUZZ = "" 
        EQ(REMDR(I, 3), 0)              :F(TRY_5)
        FIZZBUZZ = FIZZBUZZ "FIZZ" 
TRY_5   EQ(REMDR(I, 5), 0)              :F(DO_NUM)
        FIZZBUZZ = FIZZBUZZ "BUZZ"      
DO_NUM  IDENT(FIZZBUZZ, "")             :F(SHOW)
        FIZZBUZZ = I
SHOW    OUTPUT = FIZZBUZZ
        I = I + 1
        LE(I, 100)                      :S(LOOP)
END

Tags , ,  | 1 comment

Functional/Object Oriented Evolution

Posted by Daniel Lyons Thu, 04 Oct 2007 02:27:00 GMT

I noticed something interesting the other day about the functional and object-oriented languages.

Consider Python, where you can write functions like:

def foo(first, second=0, *rest, **kwargs):
  return first + second / len(rest) * kwargs['multiple']

Compare to a functional language like Haskell, where you can’t do that, but you can create types like:

data MyThing = YourThing Integer 
             | AnotherThing (Int, Char, Float)
             | NoThing

Weird that in Haskell, a functional language, you don’t get keyword arguments or arbitrary numbers of arguments, but you do in Python, whereas in Python, you’re confined to a rather small set of built-in types or full-on objects.

Of course in Lisp, you get really ornate functions and the ability to make new types through OOP. But you don’t get strong typing. ;)

Tags , , ,  | 2 comments

One More on Haskell

Posted by Daniel Lyons Fri, 28 Sep 2007 09:11:00 GMT

My faith in Haskell has wavered somewhat lately, and all because of Reddit. Essentially, three things:

  1. Haskell’s fixed-point combinator, y f = f (y f).
  2. The ramifications of Haskell’s purity on abstraction.
  3. Haskell’s epidemic academia.

The first is really two problems. One is that the usual definition of the Y-combinator is not itself recursive. Obnoxious. The second problem is that, though it’s beautiful, there are a number of problems that you can’t express in Haskell using the above combinator which you could in an untyped language. This isn’t, apparently, news to anyone but me. I can’t see how to use the above Y-combinator. This is an incredibly academic problem, like most Haskell problems, but it bugs me a little.

The second was elucidated by Peter Van Roy on Lambda the Ultimate’s forum:

“True state lets information pass “underground” between two interfaces, i.e., the information passes without any apparent connection between them. This is because the connection is the shared state, which is shared by the two interfaces yet hidden from the outside. The shared state is a kind of covert information channel: it lets a module pass information to other modules (or to itself in the future) without anybody else seeing it.”

His point has to do with the fact that Haskell’s purity means that in order to get data from some point A to some point B through a number of other modules, each of the intermediate modules will have to carry the information around, even if it doesn’t do anything with it.

I would like to hate that, really, I would. But I can’t, because I programmed Voltaire in a very functional and abstract way in PHP partly because I could count on a handful of global variables passing some state around “underground” between modules that were loosely connected. In particular, Voltaire creates a region context and a template context in which each script is evaluated. The database connection is also shared clandestinely like this. If I were using Haskell, every function in the system would have to take an extra four parameters to get the current region, template, path and database connection, even if it wasn’t going to use it or pass it directly to a child.

It’s easy to denounce. At the same time I feel blameless for having done it, because while the state is “available” to anything beneath, nothing should be changing these variables outside the core. It’s available in a read-only way. Later on I wrote a plugin for rendering templates programmatically, and that involved understanding the inner state, and another plugin for producing lists which also involved understanding the inner state. But languages like Lisp provide interesting semantics for those times when you would want to change the behavior of a global variable safely.

Which brings me back around to Lisp, the language I have paid the least attention to of the four or so that I decided were “safe” so long ago when I started to force myself to use functional programming. And, truly, there are things about Lisp which are hard to love. The principle advantage of Lisp is that it permits you syntactic innovation. It’s certainly the only language that provides it meaningfully (let us not quibble about Lisp/Scheme differences or bizarre languages like Pliant). But doesn’t this seem like the fundamental abstraction of programming languages?

Aren’t we always starting with some problem and selecting a language based on its syntactic abstraction of some part of the problem domain? I mean, I pick Lisp because I want the ability to create my own structures. That comes in handy against every problem domain. But if I want to write a blog, well, Rails makes it a lot easier up front. Maybe I don’t get linguistic abstraction, but the starting abstraction is quite close. Michael chooses REALbasic for application development, I choose Cocoa. Both of us are making certain sacrifices in the name of abstraction. I lose defmacro. He loses the most recent Cocoa innovations. If I pick Lisp for my blog, then I’m sacrificing the fast start. The laziness of later syntactic abstraction had better pay off. The up-front cost is higher. For any given reasonable language, there is going to be a situation in which its abstraction is an up-front benefit that beats the up-front cost of using Lisp.

You see, these languages are compression algorithms. They compress your explanation of how to solve a problem. Like any compression algorithm, there are problems for each language that compress so badly you wind up writing a different language inside the language. This is Greenspun’s law all over again. Lisp wins frequently because it makes it easy to create a sublanguage for a subproblem that compresses it better. Ruby wins frequently because it encompasses most problem domains.

And Haskell wins frequently in academia because academia is interested in representative subproblems rather than complete problems. My experience on the ICFP this past year was that when you give Haskell a pure math problem, it will beat nearly anything else hands-down for both readability and speed. Now sprinkle some I/O on the problem. How about a non-local return? Perhaps a bit of, dare I say it, destructive state? Suddenly you find yourself looking up “monad transformer,” wondering what the hell you got yourself into.

And Justin, G-d bless him, is a Haskell wizard. I can sort of read the code he wrote. My contribution to that code was negligible. I wasn’t much help debugging it. But at the end, we had a performant Haskell version using prominent Haskell performance themes. But it was also 300 lines of code. I remain convinced against any of that pesky proof stuff that an OCaml implementation would have been half the length, more understandable, maintainable and debuggable. I believe Justin came away from the contest with the opposite conclusion: proof, solid proof, that Haskell can be made to attack the kinds of domains that other languages are generally selected for. In other words, his success with Haskell encourages him to use it more, whereas it filled me with doubt.

And today I see on Reddit a link to the paper introducing Haskell Server Pages. Not to the code itself, or the documentation talking about it, or to a page talking about it or some examples, but to an academic paper about it. I write on Reddit that “I have to admit I’m getting a bit tired of seeing things that should be cool Haskell libraries show up as academic papers. It’s as though the whole industry has turned its gaze to Haskell, exasperatedly asking to see something practical, and instead of turning out practical things, the Haskell world turns out academic papers about practical things. “Proof” that Haskell can be used practically is not the same as people using Haskell practically, nor the same as people practically deciding to use Haskell.”

Let’s face it. I really enjoy Haskell in part because of its snobbery, but that’s really a part of what it is. And the posturing about how mind-expanding it is. Haskell is addicted to academia. You’re much more likely to see a paper about some neat library than a neat library. When you go look at the code, it’s untested, only works on one platform, is broken or partially implemented. Academics are not rewarded for having good, useful code. They’re rewarded for writing papers.

Look at the Wash page and tell me who’s going to use this framework. Look at the first four links. Notice these PDFs and PS files are all LaTeX output. What kind of web people would do this? Academic web people.

So here we are. I suppose every language has its vices. I’m particularly drawn to languages that do a lot of posturing, apparently. Beyond that I’m not sure what to conclude. Every language that isn’t pure offal (Java) seems to have a place in the world. It would be better not to get too worked up about it, but it’s probably impossible.

Tags , , , ,  | 1 comment

The Ultimate Act of Hubris?

Posted by Daniel Lyons Fri, 28 Sep 2007 08:59:00 GMT

In a sense, isn’t writing a language in itself the supremely audacious act of intellectual hubris?

“Behold, my language. You want my language so badly, you’re already going to have my language around to compile it in. My language is best for a variety of tasks—for example, see how well it works for that most important and complex of tasks, implementing my language? Don’t you want to use my language for your next project? Especially considering how much effort and hard work I donated to the world, making my language and then implementing it? Bearing in mind as you decide that my language is better than any predecessor language, because it builds on their strengths without their weaknesses? Did I mention my language is a mathematical formalism? Yeah, it is.”

Tags , ,  | 1 comment

Python: Speed and Agility

Posted by Daniel Lyons Thu, 20 Sep 2007 07:14:00 GMT

For a good time, please read a very twisted analysis of the benefits of performing poorly. (To my new anal-retentive readers: you try and figure out this guy’s name, and then I’ll cite it).

I have some remarks on Reddit which I’m proud enough of that I thought I’d share them with you sharks here. I wonder if the author has really listened to his own argument. I find it somewhat lacking in the “holding water” department:

“If you know the language to be dog slow any way, you’re much less likely to waste your time on the pointless microoptimizations that geeks so love…”

I think this one works equally well in reverse: if you know the language is already quite fast, you’re much less likely to waste time on pointless micro-optimizations that geeks so love. In fact, if your language is dog slow, there are probably more optimization opportunities than there are if it’s already quite fast, as a matter of diminishing returns.

“The fact that non-standard library code is inherently somewhat inferior adds further incentive to attempt community wide standardization…”

This fails rather spectacularly to explain why the Common Lisp standard includes such a huge standard library. Consider the inverse argument: “the fact that your library code runs as well as built-in code removes incentive to use the standard library.” In the magical imaginary world I pretend to live in, people apply appropriate abstractions without much regard for the performance—abstractions that perform radically poorly being considered inappropriate, I guess—but I would never imagine sitting on my hands, wondering whether or not I can “afford” the overhead of a function call.

“Python’s very slowness represents part of its competetive edge over languages that are in some ways better engineered and more capable…”

What is this, things that are poor in one area tend to be good in others?

I could only consider agreeing if somehow it were a zero-sum equation: all languages have equal amounts of effort applied to them, and some of it goes to performance. But it’s not a zero-sum equation, languages receive differing amounts of attention to different strengths for different reasons. Moore’s law and the rapidly increasing demand for programming alone have enabled Python to compete against a language three times older, better designed, implemented and specified.

Actually, no, that’s flat-out nonsense.

“I’d not be suprised if on the whole it greatly increases programmer productivity and results in clearer and more uniform code…”

I would tend to expect uniformity from any language which syntactically enforces it. :)

I certainly would not expect a poorly-performing program to go through as many stages of evolution as a rapid program. You have to factor in the compilation phase—but one usually doesn’t compile in-development Lisp programs while working on them. They evolve much more organically, because there’s not necessarily a separation between development and testing, whereas in Python there is to a larger extent. Supposing the compile-time differences are negligible (which, they are, if you are careful) I have a hard time believing that Python programs running more slowly permits more evolution to take place.

“Also, since only builtins have reasonable performance there’s added motiviation to become very familiar with the available builtins and far less temptation to roll one’s own version of say dict.setdefault (even if it it sucks)...”

I think the existence of sucky built-ins like setdefault contradicts the point about the quality of the standard library. From where I’m sitting, the quality of the standard library is a function of backwards-compatibility more than community involvement. Lisp has a larger, somewhat clunkier one because it is much older. Ruby’s library is much cleaner because it went through a lot of life without having to worry about backwards compatibility very much. Io is doing even better, having been designed by language aesthetes like Steve Dekorte who are familiar with a lot of good and bad APIs, and who aren’t afraid to break backward compatibility to make a global improvement in expressiveness.

This article nicely dovetails with an argument my friend Pi has been having with various cornholes on IRC about Python and Ruby. Ruby’s standard library is just plain nicer. Which do you prefer, based on your gut instinct:

foo.bar.baz.split.collect { |x| x.bazzle }.join(' ')

Or this:

' '.join([ bazzle(x) for x in string.split(foo.bar()) ])

Hell, for good measure, let’s toss in some Haskell:

unwords $ map bazzle $ words $ foo bar

I’m talking about your aesthetic reaction here. Your artistic reaction. You know which of these you prefer. You want the agglutinative glory of Ruby or the isolating awesome of Haskell. Follow your eyeballs on the above Python example. Compare to Ruby and Haskell. Your eyes hop around on the Python, trying to figure it out. Haskell and Ruby, your eyes slide across from left to right. You feel like you’re reading a language, not decoding one.

As an aside, slap the next person you meet who, ignorant of Haskell, rants about static typing. They’ve never used it, they have no idea what they’re talking about. They’re actually giddy or incensed about type annotations and other forms of busy-work. Don’t put up with it.

Tags , , , ,  | 4 comments

Object-Oriented Lisp and OCaml

Posted by Daniel Lyons Fri, 07 Sep 2007 19:12:00 GMT

I have a theory about the respective OO capabilities of Lisp and OCaml, but I’m not quite sure who to ask.

In Lisp, when you make some class and some methods, those methods look like regular functions. In fact, in a sense, they are regular functions which happen to have more than one definition depending on the types of their parameters. So functional-style programming isn’t hampered.

I’m wondering to what extent functional and OO programming styles intermingle in Lisp and OCaml. My suspicion is that OCaml suffers from a lower degree of functional/OO integration because it provides object#method syntax instead of method object syntax which would be analogous to its function call syntax and closer to Lisp’s (method object) syntax.

Tags , , , ,  | 1 comment

Knowing a Language

Posted by Daniel Lyons Fri, 07 Sep 2007 08:46:00 GMT

Apparently people are talking about what it means to know a language. The debate seems to be centered around whether or not knowing the language syntactically or in terms of the library is what really constitutes “knowing” it.

Even in natural language there is disagreement around this kind of thing. Some teachers prefer to focus on vocabulary, others on grammar. Many others prefer immersion to formal methods. I think this translates roughly to teaching programming in terms of idiomatic forms.

I’ve been exposed to all of those methods of learning a language in computer science. Lots of people buy, read and are devoted to “cookbooks.” I have found that, although they are often good, some are overly simplistic (O’reilly’s PHP Cookbook comes to mind), but neither is really a good substitute for reading actual code in the language. Immersion, if you will.

In Compiler Writing in college, we were required to implement a compiler for a language nobody knew which bore very little resemblance to other languages we knew (Tiger). In that class I got to exercise my skills at reading a BNF specification of a grammar, and I was able to write code that should have compiled pretty easily with just a little bit of studying. This doesn’t come up very often, because usually there is a body of code to learn from idiomatically. There are no idioms for Tiger, because nobody uses it enough to establish them and it’s designed to by syntactically troublesome without being particularly expressive in the first place. (I did once email the REALbasic developers to see if they would supply me with a BNF for the language, and I was told it didn’t exist). So the skill is nice to have but comes up very rarely.

The author above is, I believe, reaching for the same idea of idiomatic use in talking about max() in Python, because anyone who really understood Python would use it. At the same time, I have to say there’s more to it than that, because someone who really knew Python would know not only the idioms but also when not to use them. Lots of English speakers do not use “me” and “whom” correctly (including me, sometimes). True mastery includes that level of knowledge. For many languages, it probably includes a certain amount of knowledge about the implementation, or at least an intuition as to what equivalent constructs are going to perform faster, for example.

But taking this concept even further, I would say nobody is master of any programming language without knowing at least one other programming language. Linguistic myopia is a huge problem. Languages are primarily tools. If you think there are no better tools for any problems, you will find yourself writing unidiomatic code in your language to solve problems which are idiomatic in other languages. In the case where you’re forced to solve a problem in the wrong language, it may even be better sometimes to solve it in a way idiomatic to the more appropriate tool than heave and misapply your energy to an idiomatic but confusing solution. It certainly will be worth it for the affect on your brain.

Tags , ,  | 1 comment

Glue

Posted by Daniel Lyons Mon, 06 Aug 2007 08:55:00 GMT

I’ve been thinking about glue code a lot lately.

As a lazy programmer, I chafe at how much of my time is spent writing glue between two different systems. These two systems usually are pretty similar or I wouldn’t be bridging them. For example, for one client, I’m bridging a templating system and an API for sending email. Obvious. Another client is getting the ubiquitous bridge between the database and HTML for editing. It seems like an awful lot of this code is basically duplicated effort.

The advanced programming languages of today seem, to me, to be focused on helping you be more abstract. In some cases, this pays off immediately in terms of reducing glue. C++ may not have lambda abstractions, but at least I don’t have to write my own quicksort for my weird data structure or weird data types. In a sense, the code that normally would go between quicksort and your linked list or your array or whatever would be glue. In the best case scenario. In the average case, you’d probably wind up rewriting it altogether.

Lambda abstraction is in my opinion fundamental. Without it, there are things you simply cannot abstract out. The Y combinator may not be the most practical tool, but it’s an excellent demonstration of how powerful the tool is. There’s no way in C to abstract out recursion. Yet every language with lambda can. So there is, at least in theory, a whole category of glue that you have to duplicate in languages which lack lambda or equivalent abstraction mechanisms.

Haskell and Forth don’t have much in common, but both have a simplifying concept at the core which enables powerful abstraction of glue code. In Haskell, every function essentially is of one parameter, returning either another function of one parameter or one value. This, plus a general amenability to functional abstraction through higher-order functions like map and flip, makes it easy to stuff a function into a situation it wasn’t intended for. In PHP, you’d have to write a new function in the best case scenario, or more likely just repeat the same loop all over again.

Forth uses the simplifying concept of a stack as input and output. Does some word use a different API than is convenient for you? Just mangle the stack a little bit.

In a sense, object orientation also provides you with this power. You have an object which expects some API on some other object, but you have something that doesn’t quite fit? No problem, just inherit the right thing, fill in the methods you need and you’re set. Yet, looking at it now, from these other sides of the fence, I have to wonder if this is more powerful or more general or just wordier.

I see there being a lot of computation taking place simply to translate one thing into a different format. This has bothered me a lot for a long time. I’m no longer convinced that that step can be avoided—but if it cannot, is it at least possible to step out from between the two formats and let the computer perform the translation, without having to manually glue these systems together? It seems like many of the gains of functional programs are simply that they prevent you from being more involved in the process than you have to be. How far can we take this concept?

Tags ,  | no comments

C++ Apologetics

Posted by Daniel Lyons Mon, 11 Jun 2007 06:07:00 GMT

This is my third try to give an opinion about C++. Even having an opinion of this language is hard.

Firstly, C++ suffers from having evolved quite a bit during its life. A lot of the information out there about it is outdated. It’s not hard to find tutorials that have include files ending in .h and not adding a using namespace std; declaration. It’s also not hard to find example code for g++ that doesn’t compile.

Secondly, C++ is huge. Most people don’t use a fraction of its capabilities. A lot of its capabilities are rather new; there is a general fear of using new features for loss of compiler compatibility, which has been a problem for a long time with C++ because of the general size and complexity of the language. Even though it adds an additional pile of keywords onto C, the keywords it adds sometimes have different meanings in different contexts (virtual is the prime offender). In other places, you have to do some truly strange things to get C++ to do as you intend, pure virtual functions being defined with a tailing “= 0” in the class definition being the best example.

Partly because C++ is huge and partly because it has been around for so long, students are not exposed to all of it, or even necessarily the important parts of it. It takes a long time to learn, and with outdated or poor information at their disposal, people pick up bad habits. The popularity of the language only makes this a bigger problem.

Thirdly, and perhaps most importantly, C++ strives to maintain compatibility with C. This explains many of the faults of the language, especially the type/class dichotomy.

The language D is clearly trying to reimagine C++ without so much of the evolutionary baggage. That’s a noble goal, but by this late stage C++ compatibility is a big concern also. If you sacrifice that, you might as well sacrifice some of the other constraints and wind up with something more like OCaml.

Finally, C++ suffers from having some rather brain-dead defaults. The virtual declaration must be used extremely liberally to achieve what’s normally understood as OO—calling a function with a pointer to a parent class and having the instance’s class code get executed. The explicit keyword exists solely because the language is very friendly with implicit casting.

The more I learn about C++ , the more hilarious I find claims of Java’s superiority. If Gosling is such a genius, why did generics get added to Java so late in the game, mirroring evolutionarily their late addition to C++? I haven’t looked at it very closely, but I have trouble imagining that in Java you can do anything as cool as a templated average function which can average across any numeric-looking type in any data structure for which sequential access is possible.

I also remembered today one reason why I wanted to learn C++ back in the day: BeOS is completely written in it. Right now the only reason I can think of to use it would be to use ICE. For my purposes, C++ is basically defunct, unless I want to program in QT for some reason. Even that I’d have a hard time convincing myself to do, since I don’t care much to program for Windows or (frankly) Linux/BSD.

So again I’m having trouble trying to summarize my opinion. Could C++ have been a great language? Probably not, but it’s pretty good for what it’s trying to do. It’s not the fastest language on the planet, but it’s quite fast, often faster than its detractors claim. Much of the slow C++ out there is not using STL algorithms and data structures. Manual memory management is time consuming for programmers and the allocator thrashing can be more expensive on the processor than garbage collection, but this is largely a concession to C, and you can apparently add your own garbage collector in the form of a library anyhow. It has one of the most expressive inheritance mechanisms, perhaps second only to Eiffel’s, but comes at a cost of being harder to use (again the virtual keyword makes life interesting, along with the access control public/private/protected nightmare). It’s possible now, with the template system, to write terse, expressive code, but almost no one does, learning how is hard, and it produces code that’s quite hard to read. If you need speed, there are faster languages; if you need elegance, there are more elegant languages. Perhaps there is no alternative if compatibility is what you optimize for, but it’s hard for me to imagine wanting to start a new project in it. A truly mixed bag.

Tags , ,  | 1 comment

C++ "Generic" Programming Conundrum

Posted by Daniel Lyons Sat, 09 Jun 2007 20:01:00 GMT

Looking at C++, you sometimes see cool-looking code like this:

// load the numbers in the input file
copy(istream_iterator<double>(input), 
     istream_iterator<double>(), 
     back_inserter(numbers));

// get the sum
double sum = accumulate(numbers.begin(),
                        numbers.end(), 
                        0.0);

// compute the average
double average = sum / numbers.length();

Of course, this traverses the vector twice, so you might think of a better way to do it would be to do a right fold across the vector, adding to the sum and incrementing the count until the vector is exhausted, and then do the division. You know, take this kind of code (untested):

double sum = 0;
int count = 0;
for (vector<double>::iterator i = numbers.begin(); 
     i != numbers.end(); 
     i++)
{
  sum += *i;
  count++;
}
double average = sum / count;

In OCaml or Haskell, we’d consider converting it into a right fold along these lines:

let average l = 
  let acc (sum, count) x = (sum + x, count + 1) in
  let (sum, count) = List.fold_right acc l (0,0) in
    sum / count

But this is C++, so instead of getting a closure or a fold function, we get accumulate, which takes a start and ending iterator and an initial value. It works just like a fold, but uses operator+ if you pass in an object. So, we can create an averaging functor like this:

class averaging_accumulator : 
  public binary_function<double,
           averaging_accumulator<double>,
           averaging_accumulator<double> >
{
public:
  int count;
  double sum;

  // constructor
  averaging_accumulator() : sum(0), count(0) {}

  // compute the actual average
  double average()
  {
    return sum / count;
  }

  // implement the binary operation between the two types
  averaging_accumulator operator+(double d)
  {
    sum += d;
    count += 1;

    return *this;
  }
};

So we can now implement an average with something along these lines:

averaging_accumulator acc;
double average = accumulate(first, last, acc).average();

Of course, once we have this working we might want to make it work for floats also. So we make it into a template:

template <typename T>
class averaging_accumulator : 
  public binary_function<T, 
    averaging_accumulator<T>, 
    averaging_accumulator<T> >
{
public:
  int count;
  T sum;

  averaging_accumulator() : sum(0), count(0) {}

  // compute the actual average
  T average()
  {
    return sum / count;
  }

  // implement the binary operation between the two types
  averaging_accumulator operator+(T d)
  {
    sum += d;
    count += 1;

    return *this;
  }
};

Another thing we might want is to make a function to compute the average. And naturally we’d want to make it a nice generic function along the lines of the builtins. The best example of that kind of programming I could find led me to write this:

template<typename InputIterator, typename T >
T average(InputIterator first, InputIterator last)
{
  averaging_accumulator<T> acc;
  return accumulate(first, last, acc).average();
}

Now, what I don’t understand is that I have to call this function like so:

double avg = average<vector<double>::iterator, double>
(numbers.begin(), numbers.end());

Why do I have to include the iterator type and result type, when the internal call to accumulate does not? I couldn’t get it to compile any other way. Same thing with the other generic functions copy and transform—you never need to tell them what iterator type they’re receiving. I’d rather be able to call it like this:

double avg = average(numbers.begin(), numbers.end());

If anybody knows how to do this “the right way,” please let me know.

Update: I’ve figured out the way to do this. Replace your average function with this:

template<typename InputIterator>
typename iterator_traits<InputIterator>::value_type
average(InputIterator first, InputIterator last)
{
  averaging_accumulator<typename iterator_traits<InputIterator>::value_type> acc;
  return accumulate(first, last, acc).average();
}

In short, instead of having a type parameter T, derive it from your iterator’s type using iterator_traits. That class is parameterized by an iterator and provides a number of different type definitions, but the only one you’ll care about is value_type. Due to changes in the C++ standard, you now need a prefix typename before a template expanded type name like that, so your complete type is going to look like typename iterator_traits<InputIterator>::value_type as in the above code. Then, the compiler will be able to figure out the template parameters (but only for functions, not classes). If you have a longer function and you need the value_type more than once, you can make a typedef local to this function.

Tags , ,  | no comments

Older posts: 1 2 3 4