On Sunday, August 26, 2012 2:20:15 PM UTC-7, Bernd Paysan wrote:
> Paul Rubin wrote:
>
> > Are you talking about compiling programs of similar size, on similar
>
> > hardware, with compilers that do similar levels of optimization? It's
>
> > certainly plausible that given more CPU power, fancy compilers will
>
> > use it to do more optimization and automation, rather than just doing
>
> > the same stuff as before and finishing sooner.
>
>
>
> No, I'm comparing a bloated Java+C++ project to a non-bloated Forth
>
> project. But even when I compare two non-bloated projects, GCC is
>
> hellish slow.
>
>
>
> > Maybe you should try TCC (
tinycc.org) for development builds.
>
> > It's around 10x faster than GCC, though the output code is nowhere
>
> > near as optimized.
>
>
>
> Unfortunately, TCC is also lacking features. And using a different
>
> compiler for debug builds than for production builds is a bad idea - if
>
> you run into bugs of the production build compiler, what do you do then?
>
> The usual Forth approach to coding is making small incremental changes,
>
> because then you don't have to search long for the reasons why it
>
> doesn't work - it's very likely the small change you just did now.
>
>
>
> >> That also guides to my most-used debugging method: Inserting ~~ in
>
> >> suspicious places.
>
> >
>
> > This still takes a lot of runs of the program and possible creation of
>
> > new test cases, which can be pretty tedious.
>
>
>
> Well, it does not feel "a lot" if you can do these runs pretty quickly.
>
>
>
> >>> IMHO Haskell's type system makes it practical to program in a style
>
> >>> that while not impossible, would be much harder to manage in
>
> >>> Forth....
>
> >> I'm not sure why you claim it is error-prone and to be avoided....
>
> >> Forth is a programming language which tells you to avoid unnecessary
>
> >> abstractions.
>
> >
>
> > Right, the reason it tells you to avoid unnecessary abstractions is
>
> > that using abstraction in Forth is error-prone.
>
>
>
> No, that's not the case. You should avoid unnecessary abstractions
>
> since they don't pay off. You can use necessary abstractions, and are
>
> encouraged to do so.
>
>
>
> > I wasn't thinking really
>
> > of postpone and create/does, but more of a style where you pass around
>
> > execution tokens a lot, and those xt's may take other xt's as
>
> > arguments
>
> > to invoke over generic data structures, and so on.
>
>
>
> After I added quotations to my system, I started doing that regularly.
>
> Not exactly as in Haskell, but MINOS, using an OOP system, does that
>
> sort of stuff. Juggling around many things on the stack is bad Forth
>
> code, so pass xts around that take another xts quite likely will result
>
> in many parameters on the stack. Don't do that.
>
>
>
> The OOP system I use in MINOS handles that: You pack the several xts
>
> into one class, and use that. No need for several xts flying around,
>
> the class framework wrap them up into one entity, which is much easier
>
> to pass around.
>
>
>
> > In Haskell, it's
>
> > natural to program that way, and the code still feels solid. In
>
> > Scheme or Python you can write basically the same code, but with no
>
> > type system watching over you, it feels like walking on a tightrope
>
> > without a safety net.
>
>
>
> Don't fear the program crash! As I said, in a strong typed language, I
>
> feel like in a straightjacket. I wouldn't walk a tightrope in such a
>
> language, regardless of safety net or not.
>
>
>
> > I've programmed that way in Forth a little bit, but have been
>
> > advised against it.
>
> >
>
> >> No type system prevents programs from going wrong.
>
> >
>
> > Well, I'd say a well-typed program, including something like Lisp with
>
> > runtime type-checking, can at least have well-defined semantics, while
>
> > an untyped (e.g. Forth or C) program can go completely into the weeds
>
> > if there is a type error (such as a subscript overflow).
>
>
>
> A subscript overflow is not exactly a type error.
>
>
>
> >> It applies a number of low-hanging fruit checks,
>
> >
>
> > Haskell typechecking can be quite powerful. I like this example:
>
> >
https://gist.github.com/2659812
>
> >
>
> > Explanation: Red-black trees are binary trees with the following
>
> > invariants:
>
> > 1. Each internal node is colored either red or black.
>
> > All leaves are black and the root is black.
>
> > 2. All children of red nodes must be black. Black nodes can
>
> > have children of either or both colors.
>
> > 3. All paths from the root to the leaf contain the
>
> > same number of black nodes.
>
> >
>
> > This means the tree is approximately balanced: any path from the root
>
> > to a leaf contains n black nodes, and between 0 and n red nodes, so
>
> > the
>
> > worst case search depth is no more than 2x the best case. You can't
>
> > get
>
> > a completely lopsided tree. To insert or delete a node, you have to
>
> > do somewhat complicated juggling operations ("tree rotations") to
>
> > preserve the invariants, and it's easy to make mistakes coding these
>
> > operations. The C++ implementation that I looked at has assert
>
> > statements that check at runtime that the code didn't hit some weird
>
> > edge case that messed the
>
> > invariants up. Even after extensive testing, the assert statements
>
> > are still there in the program.
>
> >
>
> > It turns out to be pretty straightforward to express those invariants
>
> > as
>
> > a Haskell datatype (seen in the url above). Any value belonging to
>
> > the
>
> > type must be a properly balanced tree. That means that the juggling
>
> > code cannot possibly mess up the invariants. Any attempt to make an
>
> > unbalanced tree will cause a type-checking error, and the program
>
> > won't
>
> > compile.
>
>
>
> That's exactly what I don't like. This is a rather complicated thing,
>
> and I'm sure neither of us will write a perfect program first time. As
>
> I try to write incremental programs, my first approach to a r/b tree
>
> would be a general tree program, and let the tree be as unbalanced as I
>
> like. If this works, I would start adding balancing rotation
>
> operations, one after the other (you know, there are several cases you
>
> have to think about). Each of them is tested, but of course, the tree
>
> will still be lopsided.
>
>
>
> Maybe you can do that in Haskell, too, by just trying with the full
>
> blown type, and doing the test runs with a reduced type information,
>
> which doesn't care about balancing.
>
>
>
> > It would be pointless to have assert statements in the
>
> > executable since the compiler has statically verified that the
>
> > asserted
>
> > conditions hold. I would not call this low-hanging fruit. It's a
>
> > sophisticated condition being checked, that's a source of bugs in
>
> > other real-world implementations, and the compiler verifies it to
>
> > higher confidence than even quite a lot of unit tests could give.
>
> >
>
> > As an even more extreme example, Coq's type system is even stronger
>
> > than Haskell's: it can express any mathematical proposition, so it has
>
> > to be Turing complete, meaning it can't do inference and you have to
>
> > manually
>
> > supply type derivations (which is tedious). But it can encode (for
>
> > example) the semantics of assembly code fragments (Hoare triples) as
>
> > types. So if you have a compiler optimization pass that is annotated
>
> > to take one fragment and returns another fragment of the same type,
>
> > the Coq typechecker is able to verify that the input and output have
>
> > the same semantics and the optimization pass has not introduced any
>
> > semantic
>
> > bugs. I think I mentioned before, a sizable part of a C compiler has
>
> > been written that way (CompCert). This is not low hanging fruit at
>
> > all. It is probably the future, especially as better automation
>
> > becomes available for developing this sort of code.
>
>
>
> Calling this still a "type system" is a misnomer. Yes, this is no low-
>
> hanging fruit, but as you said, it is also tedious to use. And if
>
> that's useful during development depends on the error message. Let's
>
> say I have a code transformation pattern
>
>
>
> BEGIN code WHILE REPEAT -> BEGIN code UNTIL
>
>
>
> to skip the empty part between WHILE and REPEAT, the transformation is
>
> wrong, because the flag for WHILE means the opposit as for UNTIL. If
>
> the code system tells me just "your transformation is wrong", then I
>
> have no clue why, and can only guess. If I unit-test this thing, I'd
>
> give it a simple task and look at the results, and since the results are
>
> runable programs, I'll see why the two are different.
>
>
>
> >> while at the same time putting a straitjacket onto the programmer.
>
> >> At least that's how I feel when I program in a language with a strong
>
> >> type system. The compiler tells me "no, you can't do this", and "no,
>
> >> you can't do that".
>
> >
>
> > I never felt terribly straitjacketed programming in C, but I also felt
>
> > that the type system wasn't helping me that much.
>
>
>
> C has no really strong type system. A type mismatch usually results in
>
> a warning, it doesn't even fail to compile. I'm not that much
>
> complaining about C, C's type system is more a simple operator
>
> overloading system than anything else.
>
>
>
> > I was comfortable
>
> > programming with lots of void*'s, and programming in Lisp with runtime
>
> > types. Trying Haskell was really eye-opening for me. I'm a long way
>
> > from being a Haskell expert but I find it tremendously interesting and
>
> > different (whether that means "better" is of course a separate
>
> > matter).
>
>
>
> Yes, of course. It is quit different.
>
>
>
> >>>
http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/
>
> >> Performance is not a problem? Come on. I'm constantly seeing
>
> >> software which is slow as molasses,
>
> >
>
> > He's talking about compiled code with runtime type checks, vs.
>
> > compiled code that has been statically checked but otherwise does the
>
> > same stuff,
>
> > e.g. Lisp vs ML. If the compiler is any good, the performance
>
> > difference is usually a small constant factor, maybe just a few
>
> > percent. The large, order-of-magnitude slowdowns you're seeing in the
>
> > examples you mention are due to architectural issues in the slow
>
> > programs.
>
>
>
> Yes. Usually several architectural issues stacked on each others, like
>
> using a complicated delegate-style OOP program (nobody really wants to
>
> solve the problem, so it gets delegated over and over again) on top of a
>
> slow Java VM with a bad JIT - if there is a JIT at all.
>
>
>
> >> What I think would be worth to do for analytic compilers which track
>
> >> that information anyways: Have a stack effect checker.
>
> >
>
> > Yes, that would have caught my + vs. F+ bugs. StrongForth does
>
> > that sort of checking as you're probably aware.
>
>
>
> It does a bit more, and there's even a version which runs on top of
>
> Gforth.
>
>
>
> >> Warnings when it doesn't match, please, it should compile, because
>
> >> running it (with inserted ~~) will identify the problem quickly.
>
> >
>
> > GHC now has a command-line option that lets it produce executable code
>
> > even if the program has type errors. You then get a runtime error
>
> > only
>
> > if the program tries to actually evaluate an ill-typed expression.
>
>
>
> Nice. This allows you to check what actually happens.
>
>
>
> > This turns out to be handy during development even though the compiler
>
> > has
>
> > already told you where the errors are. It's sometimes useful to be
>
> > able test the successfully compiling parts of your program right away,
>
> > postponing dealing with the unsuccessful parts til later.
>
>
>
> Given that fact, I'm less opposed than before. How useful is GHCi (the
>
> interactive command line)?
>
>
>
> >> Forth's interactivity and abstraction avoidance is what makes it
>
> >> strong. Interactivity is more than just a command line promt, it
>
> >> also
>
> >> means that you can recompile quickly. "Blink", not "lunch".
>
> >
>
> > I sometimes like to imagine what it must have been like to program in
>
> > Forth or Lisp in the glory days of those languages. I'd be interested
>
> > in seeing a demo sometime (maybe on video) of someone hacking on Forth
>
> > code that does something complicated, using good interactive Forth
>
> > tools.
>
>
>
> We Forthers really try hard to not do something complicated ;-). When I
>
> implemented bigForth' fast dictionary search, I explored several
>
> options, hashing and AVL trees. AVL trees were more complicated, and
>
> they lost to hashing in every metric: Time to write, execution
>
> performance, memory usage, and bugs to fix during development.
>
>
>
> In many cases you have an option which is simple, fast, and correct.
>
> Don't try to use the other option.
>
>
>
> > When I started using Python, I found myself thinking "this is
>
> > what the old-time Maclisp hackers must have felt like". Haskell is
>
> > different, completely different, a vision as powerful as Lisp but
>
> > unlike
>
> > anything that had been used for programming before. That in some
>
> > sense outweighs any difficulties involved in trying to actually use
>
> > it. ;-)
>
>
>
> I'm still wondering how useful that would be for me. The main
>
> challanges in the programs I write are the outside world, not the inner
>
> logic of the program. E.g. take my net2o flow control, which took me
>
> quite some time to develop. The code for it is rather simple, and it
>
> always was rather simple, didn't crash or anything. That's not the
>
> challange. The challange is to fill a network with packets just fast
>
> enough, so they don't pile up in buffers, but yet leave no unused
>
> bandwidth under real world conditions, which means things like Wifi with
>
> rapidly changing quality, competing with multiple TCP/IP, BitTorrent or
>
> net2o itself. This was a serious issue which I underestimated, given
>
> how bold other people were about their results (like LEDBAT). I thought
>
> I could just copy that, but it turned out to be not good enough.
>
>
>
> A type system won't help at all here. The type system will catch the
>
> low hanging fruits like f+ vs. + or similar. I don't have problems with
>
> those, these are "declinations", and something the human brain can
>
> handle quite well (the grammar engine).
>
>
>
> --
>
> Bernd Paysan
>
> "If you want it done right, you have to do it yourself"
>
>
http://bernd-paysan.de/
How do you know which abstractions are needed and which are not?
Is the lisp and haskell claim that abstraction is most important hollow?
Are abstractions not needed many times?