Coding: Fleeting Thoughts

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Wed Aug 15, 2012 11:07 am UTC

Wow, after my previous post everyone immediately started saying "information hiding" where they previously said "encapsulation"! And the latter is used as well, with it's "proper" meaning! Thanks folks! :D
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: Coding: Fleeting Thoughts

Postby sourmìlk » Wed Aug 15, 2012 11:53 am UTC

No problem. I tend to be obsessive about things having the correct meaning, so if it bothers you, then when I'm informed of it, it will start bothering me.

Also, are there other languages that have C++'s powerful template system? I'd say the largest reason I love C++ is because I can do so much with templates. Most other languages won't even let me use non-type template parameters, and Java, being incredibly annoying, uses type erasure, meaning that I essentially can't do anything with the template system except build containers.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Wed Aug 15, 2012 2:26 pm UTC

You, sir, name? wrote:Oh god, what have I created?

At least it's not as bad as modifying the *actual* script that's running

Code: Select all

printf "echo blasphemy!\n" >> $0

User avatar
Yakk
Poster with most posts but no title.
Posts: 11115
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Wed Aug 15, 2012 2:29 pm UTC

Your encapsulated influence on discussion wasn't mentioned by the posters, however. Because it was hidden information.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Wed Aug 15, 2012 5:59 pm UTC

PM 2Ring wrote:
Ben-oni wrote: And worse, you didn't even check to see if mktemp already exists!

Um, that's `mktemp`, i.e., he's getting a new temporary file by running the mktemp command and saving its name into t.

Ooooh! So that's how backquotes work!

Obviously, my bash skills could use some work.

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Wed Aug 15, 2012 6:22 pm UTC

sourmìlk wrote:Also, are there other languages that have C++'s powerful template system? I'd say the largest reason I love C++ is because I can do so much with templates. Most other languages won't even let me use non-type template parameters, and Java, being incredibly annoying, uses type erasure, meaning that I essentially can't do anything with the template system except build containers.

Yes: Lisp.

Java/C# style generics are just a form of generic typing. C++'s template system is just a kind of weak macro system. Lisp's macro system does everything templates do, only better, and also provides the full language within that system. C++ is incredibly restrictive in comparison: where templates only apply to classes and functions, macro's apply to arbitrary units of code.

Also, languages with type inference and functional types tend to do generics better than C++/Java/C#, etc. Until you can create a templated closure in C++, it'll never have the true power of a strongly typed functional language.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: Coding: Fleeting Thoughts

Postby sourmìlk » Wed Aug 15, 2012 6:25 pm UTC

I really should learn LISP, I can never just quite get into it enough to do something. Hopefully I'll be able to take a class at some point and that will at least force an introduction.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
Robert'); DROP TABLE *;
Posts: 730
Joined: Mon Sep 08, 2008 6:46 pm UTC
Location: in ur fieldz

Re: Coding: Fleeting Thoughts

Postby Robert'); DROP TABLE *; » Wed Aug 15, 2012 6:27 pm UTC

troyp wrote:
You, sir, name? wrote:Oh god, what have I created?

At least it's not as bad as modifying the *actual* script that's running

Code: Select all

printf "echo blasphemy!\n" >> $0
Along these lines, I wrote this yesterday.

Code: Select all

public static void Assert( bool cond, string msg = "", Type ExceptionType = null )
        {
            if ( !cond )
            {
                if ( ExceptionType == null )
                {
                    ExceptionType = typeof( AssertionFailureException );
                }
               
                var sf = new StackFrame( 1 );
                string explodedMessage = string.Format( "Error at line {0}, col {1} in file {2}", sf.GetFileLineNumber(), sf.GetFileColumnNumber(), sf.GetFileName() );

                if (!ExceptionType.IsSubclassOf( typeof( Exception ) ))
                {
                    throw new ArgumentException( explodedMessage + string.Format( ": type {0} is not derived from Exception!", ExceptionType.ToString() ) );
                }               
               
                if ( !string.IsNullOrWhiteSpace( msg ) )
                    explodedMessage = explodedMessage + ":" + msg;

                object exObj = ExceptionType.GetConstructor( new Type[] { typeof( string ) } ) // All Exceptions implement a (string) constructor.
                    .Invoke( new object[] { explodedMessage } ); // Now invoke it to build the Exception object of the type we want.
                Exception KnownEx = (Exception) exObj;
                throw KnownEx;
            }
        }

It feels as though I'm doing something terribly wrong and/or heinous, but I'm not sure what. :P Am I just scared of reflection from too many DWTFs, or is there some genuine reason why the above is a bad idea?
...And that is how we know the Earth to be banana-shaped.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: Coding: Fleeting Thoughts

Postby sourmìlk » Wed Aug 15, 2012 6:32 pm UTC

I also get a bad code smell from too much reflection. It feels like somebody wasn't inheriting properly.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Wed Aug 15, 2012 6:49 pm UTC

troyp wrote:
You, sir, name? wrote:Oh god, what have I created?

At least it's not as bad as modifying the *actual* script that's running

Code: Select all

printf "echo blasphemy!\n" >> $0

Back in undergrad, someone who I sometimes hung out with was telling me that he wrote a factorial calculator in Perl. However, how it worked was by opening its own source code, modifying the number it was on now to one less, then re-running itself.

Said that most of the reason he did it was so that he could write if (10 == 0) and have the condition be true at some point. :-)

Ben-oni wrote:Ooooh! So that's how backquotes work!

Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Wed Aug 15, 2012 7:17 pm UTC

EvanED wrote:Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Yes, I worked that one out from context (and prior familiarity with the construct in other settings).

I'm also finding that mktemp without parameters just gives the usage, so... Is my flavor of mktemp just incompatible with this script, or is it genuinely broken?

Code: Select all

Alli:~ me$ mktemp
usage: mktemp [-d] [-q] [-t prefix] [-u] template ...
       mktemp [-d] [-q] [-u] -t prefix

User avatar
headprogrammingczar
Posts: 3072
Joined: Mon Oct 22, 2007 5:28 pm UTC
Location: Beaming you up

Re: Coding: Fleeting Thoughts

Postby headprogrammingczar » Wed Aug 15, 2012 7:57 pm UTC

While on the topic of spooky shell scripts, I have one with a legitimate use:

Code: Select all

#!/usr/bin/tail --lines=+3

### Install script
# Copy-paste output into another terminal to perform the installation
install depends
configure
build executable
generate some resource files
<quintopia> You're not crazy. you're the goddamn headprogrammingspock!
<Weeks> You're the goddamn headprogrammingspock!
<Cheese> I love you

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5101
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Coding: Fleeting Thoughts

Postby Xeio » Wed Aug 15, 2012 9:19 pm UTC

Robert'); DROP TABLE *; wrote:It feels as though I'm doing something terribly wrong and/or heinous, but I'm not sure what. :P Am I just scared of reflection from too many DWTFs, or is there some genuine reason why the above is a bad idea?
I'm curious what the purpose of this is. Or at least, what does this do that you need that Debug.Assert does not do?

I mean, to me this looks like it's trying to replace standard argument checks or something, but if that were the case I don't see the reason for it as it doesn't save any code and breaks normal convention. Oh, and it's probably 10x slower because of reflection, but I'm thinking that hopefully doesn't matter since these type of assertions shouldn't fail in normal operation?

User avatar
Robert'); DROP TABLE *;
Posts: 730
Joined: Mon Sep 08, 2008 6:46 pm UTC
Location: in ur fieldz

Re: Coding: Fleeting Thoughts

Postby Robert'); DROP TABLE *; » Wed Aug 15, 2012 10:51 pm UTC

Xeio wrote:
Robert'); DROP TABLE *; wrote:It feels as though I'm doing something terribly wrong and/or heinous, but I'm not sure what. :P Am I just scared of reflection from too many DWTFs, or is there some genuine reason why the above is a bad idea?
I'm curious what the purpose of this is. Or at least, what does this do that you need that Debug.Assert does not do?

I mean, to me this looks like it's trying to replace standard argument checks or something, but if that were the case I don't see the reason for it as it doesn't save any code and breaks normal convention. Oh, and it's probably 10x slower because of reflection, but I'm thinking that hopefully doesn't matter since these type of assertions shouldn't fail in normal operation?

AFAIK, Debug.Assert pops up a dialog box if the condition fails - this is less than ideal on command-line programs or libraries, and that method was written as part of the latter.

Also, I don't actually know what the standard pattern is. I know that Debug.Assert does compiler magic to only do anything if debug mode is enabled, but apart from that, is there a more useful way of doing it than "if (something's gone wrong) throw new Exception()"? (And yeah, it's structured like that so that there's no slow lookups if everything goes well.)
...And that is how we know the Earth to be banana-shaped.

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5101
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Coding: Fleeting Thoughts

Postby Xeio » Wed Aug 15, 2012 11:38 pm UTC

I'm just not sure how

Code: Select all

M.Assert(x != null, "x", typeof(ArgumentNullException));
is any better than

Code: Select all

if(x == null) throw new ArgumentNullException("x");
for example. Other than the fact that you're manually adding the stack information to the message (which is generally redundant with a thrown exception anyway). Also, manipulating the message in that way could cause strange results (like in the case of ArgumentNullException, where it only expects a parameter name).

webzter_again
Posts: 119
Joined: Sun May 27, 2012 4:37 am UTC

Re: Coding: Fleeting Thoughts

Postby webzter_again » Thu Aug 16, 2012 3:22 am UTC

Robert'); DROP TABLE *; wrote:Also, I don't actually know what the standard pattern is.


if I understand what you're after, I've seen it called the guard pattern (and you can usually find a lot of projects that have a guard.cs file hanging around somewhere). For a somewhat more invasive approach, check out Microsoft Code Contracts

For funsies... here's one that I've seen on a few projects: https://gist.github.com/3366176

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: Coding: Fleeting Thoughts

Postby Sc4Freak » Thu Aug 16, 2012 5:36 am UTC

Robert'); DROP TABLE *; wrote:
Xeio wrote:
Robert'); DROP TABLE *; wrote:It feels as though I'm doing something terribly wrong and/or heinous, but I'm not sure what. :P Am I just scared of reflection from too many DWTFs, or is there some genuine reason why the above is a bad idea?
I'm curious what the purpose of this is. Or at least, what does this do that you need that Debug.Assert does not do?

I mean, to me this looks like it's trying to replace standard argument checks or something, but if that were the case I don't see the reason for it as it doesn't save any code and breaks normal convention. Oh, and it's probably 10x slower because of reflection, but I'm thinking that hopefully doesn't matter since these type of assertions shouldn't fail in normal operation?

AFAIK, Debug.Assert pops up a dialog box if the condition fails - this is less than ideal on command-line programs or libraries, and that method was written as part of the latter.

Also, I don't actually know what the standard pattern is. I know that Debug.Assert does compiler magic to only do anything if debug mode is enabled, but apart from that, is there a more useful way of doing it than "if (something's gone wrong) throw new Exception()"? (And yeah, it's structured like that so that there's no slow lookups if everything goes well.)

Asserts and exceptions are used for different things. There's a reason why asserts throw up an obnoxious window and only work in debug mode: they're intended to catch programming errors during development. An assert essentially means, "I assert that this condition logically must be true at this point - if not, something has gone horribly wrong and the program is in an unpredictable state". An exception means "I'm unable to fulfil my contract/preconditions/postconditions". It's appropriate to catch and handle exceptions, but not assertions.

In other words, if you're having to use the code you just wrote then it means you're trying to use assertions as error handling which is wrong. Use exceptions for error handling, and typically use assertions for checking your logic and assumptions. There are obviously exceptions to that rule (no pun intended), but that's the rule of thumb.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Thu Aug 16, 2012 6:06 am UTC

So there's an interesting thing I read a long time ago that changed my view about a couple things -- assertions, primarily. It was some guy from NASA talking about how they don't do debug and release builds -- what they fly is the "debug" build, without optimization (or low optimizations, I don't really remember) and with assertions. Basically they say "why are you debugging code that isn't going to be what's run?", so all their testing and development is done on the actual flight code.

Obviously for NASA, speed is usually less of a concern (NASA probably doesn't care too much if it takes a Curiosity even an order of magnitude longer to encode a JPEG before it transmits it at the whopping bandwidth of less than 380 bytes/sec*) and reliability way way more of a concern than most software, so the degree to which they do that is... well, I was going to say "overkill for most projects", but I don't think that quite covers it. :-)

However, it did make me think a bit, and my current philosophy is that the usual convention of optimizing away assertions during release builds is in most cases going to be a premature optimization. Sure, every now and again you want to assert some property that actually takes some time to check -- but something like assert(p == null); or assert(c.size() > 0); (for containers with a constant-time size function, of course)? How often is removing that check really going to improve performance in a measurable way?

So basically, I tend to build release builds with assertions as well, and often use my own assert macro (in C/C++) so it's not affected by NDEBUG. For larger projects where I feel like being more dilligent, I'll even set up a tiered assertion system where I'll put the occasional slow assertion in a SLOW_ASSERT macro and the rest in FAST_ASSERT, and then release builds are built with SLOW_ASSERT turned off but FAST_ASSERT left on.

It also means that you may want to set up assertion violations so you do something more graceful than just crash.

*According to dpreview, Curiosity shares a 250 megabit/day = 380 bytes/sec link with several other Mars observatories.

User avatar
PM 2Ring
Posts: 3701
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Coding: Fleeting Thoughts

Postby PM 2Ring » Thu Aug 16, 2012 6:41 am UTC

Ben-oni wrote:
EvanED wrote:Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Yes, I worked that one out from context (and prior familiarity with the construct in other settings).

I'm also finding that mktemp without parameters just gives the usage, so... Is my flavor of mktemp just incompatible with this script, or is it genuinely broken?

Code: Select all

Alli:~ me$ mktemp
usage: mktemp [-d] [-q] [-t prefix] [-u] template ...
       mktemp [-d] [-q] [-u] -t prefix


I agree with EvanED re: $(...) being better than backticks; backticks have a nasty habit of becoming invisible, or being mistaken for single quotes. :) IIRC, backticks are deprecated in bash. OTOH, I do use them occasionally for quick & dirty stuff.

Your mktemp is not a standard GNU mktemp. It shouldn't require any options or parameters.
http://linux.die.net/man/1/mktemp

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Thu Aug 16, 2012 6:42 am UTC

Erm. That sort of behavior puzzles me. Well, at least the terminology does. Assert is, most definitely, a debugging tool. It has no place in production. After all, assert(p) means "p is true". While it's a useful way to talk to programmers reading the code, and a sanity check during debugging, it has absolutely no meaning during execution. It's not an expression with useful type information, it's just a statement that doesn't do anything except crash the program if it fails.

What you're talking about sounds like error logging, which most definitely does have a place in production. Whether the error comes from bad inputs or bad code, logging it and doing the right thing is always good practice, but it's most definitely not an assertion. If the statement does have some side effect, then it can't have been an assertion.

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: Coding: Fleeting Thoughts

Postby Sc4Freak » Thu Aug 16, 2012 7:39 am UTC

EvanED wrote:However, it did make me think a bit, and my current philosophy is that the usual convention of optimizing away assertions during release builds is in most cases going to be a premature optimization. Sure, every now and again you want to assert some property that actually takes some time to check -- but something like assert(p == null); or assert(c.size() > 0); (for containers with a constant-time size function, of course)? How often is removing that check really going to improve performance in a measurable way?

So basically, I tend to build release builds with assertions as well, and often use my own assert macro (in C/C++) so it's not affected by NDEBUG. For larger projects where I feel like being more dilligent, I'll even set up a tiered assertion system where I'll put the occasional slow assertion in a SLOW_ASSERT macro and the rest in FAST_ASSERT, and then release builds are built with SLOW_ASSERT turned off but FAST_ASSERT left on.


Clearly you've never tried MSVC's implementation of the STL in debug mode. In one of their older versions, I recall they somehow managed to make std::vector's destructor run in O(n3) time in debug mode. :lol:

But anyway, the idea behind an assertion is that if you hit one, there's not a whole lot you can do about it. It means some basic assumption about the state of your program has been violated, which means you can't really do much except terminate. So I think assertions in release builds are okay if you never actually try to actually handle assertion errors and just crash, which may be preferable to silently continuing in an undefined program state.

User avatar
jaap
Posts: 2094
Joined: Fri Jul 06, 2007 7:06 am UTC
Contact:

Re: Coding: Fleeting Thoughts

Postby jaap » Thu Aug 16, 2012 8:05 am UTC

FT: Fun tricks with C++ style comment syntax:

Code: Select all

//*
your code goes here
//*/

By removing the first slash character, the code gets commented out.


Code: Select all

//*
your first code block goes here
/*/
your second code block goes here
//*/

Here the first code block is active, the second is commented out.
By removing the first slash character, the second block becomes active, and the first block gets commented out.


P.S. I don't recommend you actually use these tricks, except temporarily, and then only if you use an ide/editor with syntax colouring.

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Thu Aug 16, 2012 9:39 am UTC

Ben-oni wrote:
sourmìlk wrote:Also, are there other languages that have C++'s powerful template system? I'd say the largest reason I love C++ is because I can do so much with templates. Most other languages won't even let me use non-type template parameters, and Java, being incredibly annoying, uses type erasure, meaning that I essentially can't do anything with the template system except build containers.

Yes: Lisp.

Java/C# style generics are just a form of generic typing. C++'s template system is just a kind of weak macro system. Lisp's macro system does everything templates do, only better, and also provides the full language within that system. C++ is incredibly restrictive in comparison: where templates only apply to classes and functions, macro's apply to arbitrary units of code.

Also, languages with type inference and functional types tend to do generics better than C++/Java/C#, etc. Until you can create a templated closure in C++, it'll never have the true power of a strongly typed functional language.

I was going to say "Haskell, OCaml and D", but I'm ready to believe that Lisp's metaprogramming facilities are even more powerful.

I think your grouping of C++ with Java and C# on the one hand versus Haskell and OCaml on the other hand is unfair, though. C++ belongs together with Haskell and OCaml, even though the latter two have more elegant type inference. C# generics and Java generics are seriously handicapped compared to those of C++ and companions.

Also, the requirement that C++ should have templated closures in order to rank up with Haskell etcetera seems unreasonable. C++ generics are fully Turing-complete, so equivalent to Haskell's already. Haskell's superior type inference only adds convenience, not power. The same applies to typeclasses (concepts), by the way.

TL;DR: there are applications of type-generic metaprogramming that you can do in C++ but not in Java or C#. There are no such applications that you can do in Haskell or OCaml but not in C++ (though it will often be less elegant in C++). Lisp can do all of it and more (assuming that Ben-oni is right about that).


jaap wrote:

Code: Select all

//*
your first code block goes here
/*/
your second code block goes here
//*/

Nice trick. You can generalize this one to any number of blocks, alternating between active and commented out.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Thu Aug 16, 2012 11:10 am UTC

sourmìlk wrote:I really should learn LISP, I can never just quite get into it enough to do something. Hopefully I'll be able to take a class at some point and that will at least force an introduction.

You can learn the basics (especially of something like Scheme) *very* quickly. Try it, you'll be surprised. Of course, you'd need to learn various libraries to make practical applications, but Lisp is useful without them. Personally, I've never learned enough Scheme/Racket to do anything "practical" (Python always seems like the best option), but I still use Scheme to "try stuff out". Algorithms, data structures, programming language constructs, that kind of thing. If you've ever felt like, say, prototyping your own class system (as an example), Lisp is one of the (arguably *the*) best language for doing it.

EvanED wrote:Back in undergrad, someone who I sometimes hung out with was telling me that he wrote a factorial calculator in Perl. However, how it worked was by opening its own source code, modifying the number it was on now to one less, then re-running itself.

Said that most of the reason he did it was so that he could write if (10 == 0) and have the condition be true at some point. :-)

:-) You know, I've always wondered if self-modifying code was underutilized. I know it sounds crazy...the problems are obvious, but I'm just not so sure the problems are *insoluble* (or not worth solving). I just have an instinct that if someone worked out a more...disciplined way of doing it, it might turn out to be useful.

Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Yeah, using unpaired delimiters for command substitution was definitely shortsighted, but given that we're stuck with them, we might as well as make use of them. They're often easier to read at least (especially if you don't have syntax highlighting).

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Thu Aug 16, 2012 5:09 pm UTC

Jplus wrote:Also, the requirement that C++ should have templated closures in order to rank up with Haskell etcetera seems unreasonable. C++ generics are fully Turing-complete, so equivalent to Haskell's already. Haskell's superior type inference only adds convenience, not power. The same applies to typeclasses (concepts), by the way.

TL;DR: there are applications of type-generic metaprogramming that you can do in C++ but not in Java or C#. There are no such applications that you can do in Haskell or OCaml but not in C++ (though it will often be less elegant in C++). Lisp can do all of it and more (assuming that Ben-oni is right about that).

The problem isn't that you can't do interesting things in C++. And while theoretically Turing complete, that doesn't mean it can perform arbitrary manipulations within arbitrary contexts. It's actually rather restrictive when compared to true macros. So, it's probably worth noting exactly what the limitations are. Consider this (Haskell) code:

Code: Select all

doStuff :: MyType -> IO ()
intOp :: Int -> MyType
stringOp :: String -> MyType

behave :: Int -> IO ()
behave n = let
  f g = (sequence_ . replicate n) . (doStuff . g)
  g = f intOp
  h = f stringOp in
    do {g 1; g 2; g 3; h "1", h "2", h "3"}


Let's try writing this in C++11:

Code: Select all

void doStuff(MyType);
MyType intOp(int);
MyType stringOp(std::string);

void behave(int n) {
  auto f = [n](std::function<void(std::function(T(MyType)))> g) {      // Whoops! T is undefined!
    return [g, n](T t) {
      for (int i=0; i<n; i++){
        doStuff(g(t));
      }
    };
  };
  auto g = f<int>(intOp);
  auto h = f<std::string>(stringOp);
  g(1); g(2); g(3);
  h("1"); h("2"); h("3");
}


Since templates have to be instantiated at the time of variable declaration (because an actual type is needed), f in this example can't be made to accept two kinds of functions. There may be a workaround, but it will probably be pretty ugly. So functions and variables aren't really on the same playing field: functional variables can't be generic in the same manner as functions. C++11 has made progress, but they haven't gone far enough.

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Thu Aug 16, 2012 7:26 pm UTC

troyp wrote:
Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Yeah, using unpaired delimiters for command substitution was definitely shortsighted, but given that we're stuck with them, we might as well as make use of them. They're often easier to read at least (especially if you don't have syntax highlighting).


Yeah. I typically only use backticks for simple commands at the top level (not inside of strings or stuff like that), usually without arguments, or with very trivial arguments.
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Fri Aug 17, 2012 12:22 am UTC

Jplus wrote:I was going to say "Haskell, OCaml and D", but I'm ready to believe that Lisp's metaprogramming facilities are even more powerful.

I think your grouping of C++ with Java and C# on the one hand versus Haskell and OCaml on the other hand is unfair, though. C++ belongs together with Haskell and OCaml, even though the latter two have more elegant type inference. C# generics and Java generics are seriously handicapped compared to those of C++ and companions.

Also, the requirement that C++ should have templated closures in order to rank up with Haskell etcetera seems unreasonable. C++ generics are fully Turing-complete, so equivalent to Haskell's already. Haskell's superior type inference only adds convenience, not power. The same applies to typeclasses (concepts), by the way.

TL;DR: there are applications of type-generic metaprogramming that you can do in C++ but not in Java or C#. There are no such applications that you can do in Haskell or OCaml but not in C++ (though it will often be less elegant in C++). Lisp can do all of it and more (assuming that Ben-oni is right about that).

I think you're conflating generics with metaprogramming. They're not the same thing, they just tend to coincide in C++.

Metaprogramming means (loosely) writing programs that write other programs. At it's most trivial exreme, I guess you'd include C-style preprocessing (although that's not what you'd normally mean). C++ templates are metaprogramming, (being type-generic programs that produce type-specific programs) although I don't know the details of how they work. Then you have the powerful and expressive macro systems you see in Lisp. They're not meant specifically for generics, although that's one thing you can do with them. They're basically designed to perform arbitrary code at compile time (ie. anything that *can* be done at compile time). You *could* do that with C++ templates, but it's not what they're made for. Typically, metaprogramming is "executed" at compile time to accomplish useful computation. (Although it would also make sense to talk of runtime metaprogramming, in the case of, say a code generator - in that case your "runtime" equates to "(pre-)compile-time" for the eventual program.)

Generics means writing code that can apply to multiple types. The types are specified as "generic types" and later "instantiated" into specific types. Generics in most languages aren't considered metaprogramming since they can't really transform programs or perform compile-time computation.

So standard Haskell for example, is not considered to have genericsmetaprogramming, although they are provided by "Template Haskell" and other extensions. Mind you, there are some tricks where you force the type inferencer to perform computation for you which I would say qualify as metaprogramming. In fact, they seem similar to C++ template metaprogramming in that you force the compiler to do your computations in order to be able do its job.

PS. The reason Lisp macros are powerful is that you can use the *entire language* to write them, and the entire language as the "target", ie. you can generate *any* code and do it basically the way you'd do anything else. The reason this is possible is that the syntax of the language is basically nothing more than parentheses delimiting its fundamental datastructure (the list, of course). This means you can build code as easily as you can build a list. And you can build a list very easily in Lisp ;-) So this is why there are parentheses everywhere. We don't *really* like having them (although you do grow fond of them) - it's just that the benefits they bring are too great to give up.

edit: s/generics/metaprogramming/ in "not considered to have generics"
Last edited by troyp on Fri Aug 17, 2012 11:04 am UTC, edited 1 time in total.

Rysto
Posts: 1460
Joined: Wed Mar 21, 2007 4:07 am UTC

Re: Coding: Fleeting Thoughts

Postby Rysto » Fri Aug 17, 2012 2:03 am UTC

PM 2Ring wrote:Your mktemp is not a standard GNU mktemp

Speaking as someone who mostly works with systems that don't include the GNU variants of the various Unix utilities, I'd just like to say that the phrase "standard GNU" X fills me with rage.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: Coding: Fleeting Thoughts

Postby sourmìlk » Fri Aug 17, 2012 3:05 am UTC

I kind of like all the parentheses in LISP. They're indicative of the fact that LISP has essentially one syntactic structure: the S-expression. I like that elegance.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Fri Aug 17, 2012 3:32 am UTC

Big massive post coming along?

Ben-oni wrote:Erm. That sort of behavior puzzles me. Well, at least the terminology does. Assert is, most definitely, a debugging tool. It has no place in production.

And letting your program just continue on blithley when you know it's in a bad state is?

After all, assert(p) means "p is true".

If that's what it means, why did you put it there (or at least why do you put it in executable code instead of in a comment)?

Because that's not actually what it means; what it means is "make sure this is true and do something at least somewhat annoying if it isn't."

But anyway, I think our disagreement may just be one of terminology; to me, "halt the program" isn't the hallmark of what makes an assertion an assertion. (After all, that's not what assert does in many languages.) If you have a check for a condition that you expect to be true regardless of input, to me that's an assertion (at least in the common programming sense, which is how we're using it here) regardless of whether the program just halts, throws an exception, logs the error and does one of those things, etc.

Basically, all I'm saying is that assertions represent "this better be true here or something is probably going to go awry" checks, and that whether you call them assertions or not, why remove them during production?

JPlus wrote:I think your grouping of C++ with Java and C# on the one hand versus Haskell and OCaml on the other hand is unfair, though. C++ belongs together with Haskell and OCaml, even though the latter two have more elegant type inference.

Personally, I would say that C++ is sorta in between. Partially because of the type inference thing (I don't consider C++ to really have type inference even though templates do something related and you can sorta argue that auto does it), but also because my quick thinking right now is that Haskell and ML just have strictly more general types. (For instance, an ML function can take as a parameter both polymorphic and monomorphic functions -- which you can't do in C++. I didn't look closely enough at Ben-oni's code (I'm not familiar enough with Haskell to understand stuff without looking for some time) to know exactly what he demonstrates and how it relates.)

troyp wrote:
Also, just if you don't know, $(...) does the same thing as backticks, except they can be nested and you can tell open from close easier and they're better. </troll, sort of>

Yeah, using unpaired delimiters for command substitution was definitely shortsighted, but given that we're stuck with them, we might as well as make use of them. They're often easier to read at least (especially if you don't have syntax highlighting).

If I touch something hot, I don't say "might as well keep my hand here" ;-). And which do you say are easier to read?

troyp wrote:PS. The reason Lisp macros are powerful is that you can use the *entire language* to write them, and the entire language as the "target" ... The reason this is possible is that the syntax of the language is basically nothing more than parentheses delimiting its fundamental datastructure

This is nitpicking, but I would say that's not quite true. Rather, that's the reason that macros mesh so nicely with the rest of the language and are easy to use. You could have more complicated syntax but define an AST representation that you could inspect and produce and wind up with a language with macros equally as powerful as Lisp. (And my impression is there are a couple systems that do this, like OCaml+CamlP4, though I haven't used any outside of Lisp.) However, those AST representations would be something additional to learn.

Rysto wrote:
PM 2Ring wrote:Your mktemp is not a standard GNU mktemp

Speaking as someone who mostly works with systems that don't include the GNU variants of the various Unix utilities, I'd just like to say that the phrase "standard GNU" X fills me with rage.

I was also going to point out that GNU isn't standard in the Unix world. :-)
Last edited by EvanED on Fri Aug 17, 2012 3:38 am UTC, edited 1 time in total.

User avatar
PM 2Ring
Posts: 3701
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Coding: Fleeting Thoughts

Postby PM 2Ring » Fri Aug 17, 2012 3:35 am UTC

Rysto wrote:
PM 2Ring wrote:Your mktemp is not a standard GNU mktemp

Speaking as someone who mostly works with systems that don't include the GNU variants of the various Unix utilities, I'd just like to say that the phrase "standard GNU" X fills me with rage.


Sorry, Rysto (and EvanED). In that post I wasn't trying to imply that GNU is (or ought to be) the standard for *nix systems, just that his version of mktemp didn't conform to GNU. Sometimes the GNU man pages mentions how the GNU version of a command differs from other *nix implementations, but in the case of mktemp it does not. But from a quick Googling, it appears that the (original ?) BSD version of mktemp requires a filename template.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Fri Aug 17, 2012 10:17 am UTC

EvanED wrote:If I touch something hot, I don't say "might as well keep my hand here" ;-).

I don't really see them as harmful. Just wasteful. They can't do their job properly, so they've had to be replaced by proper nestable construct and are now redundant.
And which do you say are easier to read?

Well, I was saying backticks often are, but of course they're often harder as well. I mean, if you've got a longer command, for example, it might be better to have parens around it so you can easily see where it ends. For a short command, OTOH, $() is often just annoying clutter, particularly if they're the same colour as their contents. Personally, I think what Y,S,N? said above about his usage of `` vs $() makes a lot of sense, but it probably depends on the person.

troyp wrote:PS. The reason Lisp macros are powerful is that you can use the *entire language* to write them, and the entire language as the "target" ... The reason this is possible is that the syntax of the language is basically nothing more than parentheses delimiting its fundamental datastructure

This is nitpicking, but I would say that's not quite true. Rather, that's the reason that macros mesh so nicely with the rest of the language and are easy to use. You could have more complicated syntax but define an AST representation that you could inspect and produce and wind up with a language with macros equally as powerful as Lisp. (And my impression is there are a couple systems that do this, like OCaml+CamlP4, though I haven't used any outside of Lisp.) However, those AST representations would be something additional to learn.

Yeah, I guess that's an accurate distinction. I tend to see the line between "power" and "ease of use" as a bit blurry, but it *is* a line. With another AST-based macro system you could probably do anything Lisp macros do with a constant-factor blowup in code size/complexity, whereas with a less powerful macro system the situation would get worse the harder the problem you were trying to solve. With Lisp, the "AST representation" used is simply a nested list, which makes macros amazingly easy. I haven't used these other macro systems, but I suspect they're considerably more trouble...although I'd stil like to see them in more languages!

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Fri Aug 17, 2012 11:45 am UTC

troyp wrote:Yeah, I guess that's an accurate distinction. I tend to see the line between "power" and "ease of use" as a bit blurry, but it *is* a line. With another AST-based macro system you could probably do anything Lisp macros do with a constant-factor blowup in code size/complexity, whereas with a less powerful macro system the situation would get worse the harder the problem you were trying to solve. With Lisp, the "AST representation" used is simply a nested list, which makes macros amazingly easy. I haven't used these other macro systems, but I suspect they're considerably more trouble...although I'd stil like to see them in more languages!

That's... not actually correct.

Normal lisp macros operate on s-expressions, and that's how we usually think about them: after the parse tree is created, the macros run.

But it's wrong. Common Lisp's reader macros actually trigger at parse time, controlling how the parse tree is generated. I'm not a perl junkie, but I've heard you can do similar things in that language as well.

Today's reason Lisp is cool: "I heard you liked macros, so I put some macros in your macros, so you could transform code while you're transforming code!"

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Fri Aug 17, 2012 11:47 am UTC

Ben-oni wrote:

Code: Select all

doStuff :: MyType -> IO ()
intOp :: Int -> MyType
stringOp :: String -> MyType

behave :: Int -> IO ()
behave n = let
  f g = (sequence_ . replicate n) . (doStuff . g)
  g = f intOp
  h = f stringOp in
    do {g 1; g 2; g 3; h "1", h "2", h "3"}


Code: Select all

void doStuff(MyType);
MyType intOp(int);
MyType stringOp(std::string);

void behave(int n) {
  auto f = [n](std::function<void(std::function(T(MyType)))> g) {      // Whoops! T is undefined!
    return [g, n](T t) {
      for (int i=0; i<n; i++){
        doStuff(g(t));
      }
    };
  };
  auto g = f<int>(intOp);
  auto h = f<std::string>(stringOp);
  g(1); g(2); g(3);
  h("1"); h("2"); h("3");
}

In your Haskell code the type of f is (a -> MyType) -> a -> IO (), as it should, but in your C++ translation you've erratically changed it into ((MyType -> a) -> IO ()) -> a -> IO () (you also accidentally typed parentheses instead of pointy brackets around the type argument of the innermost std::function, but fortunately that doesn't lead to ambiguity). This in turn led to some confusion in the application of f to intOp and stringOp.

Here's what I think would be the most literal correct translation:

Code: Select all

void doStuff(MyType);
MyType intOp(int);
MyType stringOp(std::string);

template <class T>
std::function<void(T)> f (std::function<MyType(T)> g, int n) {
    return [g, n](T t) {
        for (int i=0; i<n; i++){
            doStuff(g(t));
        }
    };
}

void behave(int n) {
    auto g = f(intOp, n);
    auto h = f(stringOp, n);
    g(1); g(2); g(3);
    h("1"); h("2"); h("3");
}

The outermost lambda is replaced by a "regular" metafunction outside of behave, but other than that it's pretty much exactly what the Haskell code is doing. It isn't any more verbose than your first attempt, either. While C++ obviously doesn't have templated closures, this example doesn't seem to demonstrate that the absense is a problem.

Edit: forgot to pass n to f in the new translation.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

Ben-oni
Posts: 278
Joined: Mon Sep 26, 2011 4:56 am UTC

Re: Coding: Fleeting Thoughts

Postby Ben-oni » Fri Aug 17, 2012 12:08 pm UTC

Jplus wrote:In your Haskell code the type of f is (a -> MyType) -> a -> IO (), as it should, but in your C++ translation you've erratically changed it into ((MyType -> a) -> IO ()) -> a -> IO () (you also accidentally typed parentheses instead of pointy brackets around the type argument of the innermost std::function, but fortunately that doesn't lead to ambiguity). This in turn led to some confusion in the application of f to intOp and stringOp.

Sorry, my error was writing "(T(MyType))" instead of "<MyType(T)>". Fortunately this was for demonstration purposes only.

While C++ obviously doesn't have templated closures, this example doesn't seem to demonstrate that the absense is a problem.

Edit: forgot to pass n to f in the new translation.

Thank you for demonstrating why lexical closures are desirable, and why this actually is a weakness in the language. Yeah, you can work around it, but it's better not to have to. The C++ language designers certainly could have offered template-capable inner function definitions that support lexical scoping. It's not a huge step. I suspect temporary brain damage prevented them from doing so.

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Fri Aug 17, 2012 1:17 pm UTC

To go as concrete as possible, you're now claiming that the as of yet impossible

Code: Select all

void behave(int n) {
    template <class T>
    auto f = [n] (std::function<MyType(T)> g) {
        return [g, n](T t) {
            for (int i=0; i<n; i++){
                doStuff(g(t));
            }
        };
    };
   
    auto g = f(intOp);
    auto h = f(stringOp);
    g(1); g(2); g(3);
    h("1"); h("2"); h("3");
}

would be fundamentally better than the currently supported

Code: Select all

template <class T>
std::function<void(T)> f (std::function<MyType(T)> g, int n) {
    return [g, n](T t) {
        for (int i=0; i<n; i++){
            doStuff(g(t));
        }
    };
}

void behave(int n) {
    auto g = f(intOp, n);
    auto h = f(stringOp, n);
    g(1); g(2); g(3);
    h("1"); h("2"); h("3");
}

.

Apart from the facts that (1) these options are about equally verbose, (2) you need to pass n explicitly to f in both cases (as a capture in the first case, as an argument in the second) and (3) they're equivalent semantically as well as performancewise, I disagree because I think the latter is really better. In the latter case, you can generalise further from f to create code that can be reused ad inifitum (as illustrated below), while in the former case any attempt at reuse of the patterns will be forced to duplicate code. It's probably a good thing that closures can't be templated in C++, because it would otherwise tempt programmers to overuse lambdas. Sometimes information hiding is undesirable! :)

Code: Select all

template <class T, class U, class V>
std::function<T(V)> compose (std::function<T(U)> f, std::function<U(V)> g) {
    return [f, g](V v) {
        f(g(v));
    };
}

template <class T>
std::function<void(T)> call_repeatedly (std::function<void(T)> f, int n) {
    return [f, n](T t) {
        for (int i=0; i<n; i++)
            f(t);
    };
}

void behave(int n) {
    auto g = call_repeatedly(compose(doStuff, intOp), n);
    auto h = call_repeatedly(compose(doStuff, stringOp), n);
    g(1); g(2); g(3);
    h("1"); h("2"); h("3");
}

Note: by throwing std::bind and/or variadic templates into the mix you could get something even more general. With fewer lambdas, at that.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

User avatar
Robert'); DROP TABLE *;
Posts: 730
Joined: Mon Sep 08, 2008 6:46 pm UTC
Location: in ur fieldz

Re: Coding: Fleeting Thoughts

Postby Robert'); DROP TABLE *; » Fri Aug 17, 2012 10:31 pm UTC

Xeio wrote:I'm just not sure how

Code: Select all

M.Assert(x != null, "x", typeof(ArgumentNullException));
is any better than

Code: Select all

if(x == null) throw new ArgumentNullException("x");
for example. Other than the fact that you're manually adding the stack information to the message (which is generally redundant with a thrown exception anyway). Also, manipulating the message in that way could cause strange results (like in the case of ArgumentNullException, where it only expects a parameter name).

It was originally designed as simply

Code: Select all

M.Assert(x!=null, "x was null");
but then I read the spec for the interface I was implementing more carefully and realized that throwing ArgumentNullException was required, hence the kludge. I didn't realize that ArgumentException took just a parameter name, which I suppose makes the whole exercise a bit pointless.

webzter_again wrote:if I understand what you're after, I've seen it called the guard pattern (and you can usually find a lot of projects that have a guard.cs file hanging around somewhere). For a somewhat more invasive approach, check out Microsoft Code Contracts

For funsies... here's one that I've seen on a few projects: https://gist.github.com/3366176

Oh, that's interesting, especially since I've been looking at languages that are more formally verifiable than normal C#. I'll have to have a closer look at that later.

Sc4Freak wrote:Asserts and exceptions are used for different things. There's a reason why asserts throw up an obnoxious window and only work in debug mode: they're intended to catch programming errors during development. An assert essentially means, "I assert that this condition logically must be true at this point - if not, something has gone horribly wrong and the program is in an unpredictable state". An exception means "I'm unable to fulfil my contract/preconditions/postconditions". It's appropriate to catch and handle exceptions, but not assertions.

In other words, if you're having to use the code you just wrote then it means you're trying to use assertions as error handling which is wrong. Use exceptions for error handling, and typically use assertions for checking your logic and assumptions. There are obviously exceptions to that rule (no pun intended), but that's the rule of thumb.

Well, I've only been using it so far for conditions that are a developer mistake, rather than foreseeable problems, but I've personally been including null/invalid arguments as a developer mistake. I always used exceptions for the "anticipated bug" type of error, like objects returning error conditions. I'll keep what you've said in mind, since I wasn't very clear on the difference before.
...And that is how we know the Earth to be banana-shaped.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: Coding: Fleeting Thoughts

Postby sourmìlk » Sat Aug 18, 2012 8:04 am UTC

You should only use exceptions when you can't handle the error within the scope the error occurs. So, if you have a null object when you shouldn't, you only need to use an exception if the function it's in can't deal with it. Otherwise, handle it within the function. "Anticipated bug"s can probably be handled within the function: if you anticipate them, you likely have a protocol for dealing with them that you already know.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Sat Aug 18, 2012 1:31 pm UTC

Ben-oni wrote:
troyp wrote:Yeah, I guess that's an accurate distinction. I tend to see the line between "power" and "ease of use" as a bit blurry, but it *is* a line. With another AST-based macro system you could probably do anything Lisp macros do with a constant-factor blowup in code size/complexity, whereas with a less powerful macro system the situation would get worse the harder the problem you were trying to solve. With Lisp, the "AST representation" used is simply a nested list, which makes macros amazingly easy. I haven't used these other macro systems, but I suspect they're considerably more trouble...although I'd stil like to see them in more languages!

That's... not actually correct.

Normal lisp macros operate on s-expressions, and that's how we usually think about them: after the parse tree is created, the macros run.

But it's wrong. Common Lisp's reader macros actually trigger at parse time, controlling how the parse tree is generated. I'm not a perl junkie, but I've heard you can do similar things in that language as well.

disclaimer: I know next to nothing about the "AST-based" macro systems we were discussing. I've heard of them, but have no experience with them at all.

So, you're saying because Lisp macros can control the generation of the parse tree, they're more powerful than macros that manipulated an already generated parse tree? Lisp macros are certainly nicer, but it seems to me EvanED is right when he claims the AST-macros would still be fundamentally as powerful. What could you do to change the generation of the parse tree that you couldn't do by manipulating it once it's made? The Lisp syntax just represents a parse tree anyway. It seems to me the macro manipulations would be doing the same thing in both cases, but the advantage in Lisp is that the representation of the AST you deal with is actually the original syntax of the language. Which *is* a big advantage, since instead of having to deal with the syntax of the language-turned into a parse tree-represented as a data structure in the language, you just have to deal with sexprs, period. But is there anything you really *couldn't* do with the AST-macros?

Mind you, I'm assuming here the other systems have access to AST representations at runtime, as opposed to constructing and manipulating them at compile time. Purely compile-time macros surely wouldn't be as powerful. EvanED mentioned camlp4, which is called a preprocessor, which makes it sound like that at least operates at compile time (but I really don't know).

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Sat Aug 18, 2012 3:57 pm UTC

troyp wrote:
EvanED wrote:If I touch something hot, I don't say "might as well keep my hand here" ;-).

I don't really see them as harmful. Just wasteful. They can't do their job properly, so they've had to be replaced by proper nestable construct and are now redundant.

My comment was a bit tongue-in-cheek of course, though I think it's partially true. For a couple reasons, I view the redundancy itself as harmful.

With another AST-based macro system you could probably do anything Lisp macros do with a constant-factor blowup in code size/complexity

I'm not even convinced it needs to be that much of a blowup, at least in a language with pattern matching. But I haven't really used a system like CamlP4 either, just read a tiny bit about it a while back, so I can't comment very intelligently either. :-)

troyp wrote:So, you're saying because Lisp macros can control the generation of the parse tree, they're more powerful than macros that manipulated an already generated parse tree? Lisp macros are certainly nicer, but it seems to me EvanED is right when he claims the AST-macros would still be fundamentally as powerful. What could you do to change the generation of the parse tree that you couldn't do by manipulating it once it's made? The Lisp syntax just represents a parse tree anyway. It seems to me the macro manipulations would be doing the same thing in both cases, but the advantage in Lisp is that the representation of the AST you deal with is actually the original syntax of the language. Which *is* a big advantage, since instead of having to deal with the syntax of the language-turned into a parse tree-represented as a data structure in the language, you just have to deal with sexprs, period. But is there anything you really *couldn't* do with the AST-macros?

So Ben-oni makes a good point, which is that Common Lisp reader macros are more powerful still from a point of view, because (I think) they can accept literally arbitrary syntax (well, you need a specific two-character lead to trigger the reader macro) -- the input doesn't have to be syntactically well-formed according to the usual Lisp rules. For instance, you could set a reader macro to read #complex<1, 2> to a complex number -- but this example is more well-behaved than it needs to be; It think you could accept mismatched parentheses and all sorts of crap with a reader macro.

However, I feel fairly confident saying that when people talk about Lisp "macros", they're almost never including reader macros in that, only defmacro/syntax-case-style macros. In fact, the Common Lisp Hyperspec even says "A reader macro is not a kind of macro" and doesn't include the former in the definition of the latter.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests