![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
http://landoflisp.com/
This is a very fun webcomic turned into book, go well down into the rabbit hole
to see what it is all about.
I believe I posted before a previous chapter. While it uses Lisp as a vehicle,
it's actually a fun read to anyone into programming... :)
It's the brainchild of Conrad Barski, from lisperati. He also has something to
say about haskell:
http://lisperati.com/haskell/
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
On 29/10/2010 01:12 AM, nemesis wrote:
> http://landoflisp.com/
>
> This is a very fun webcomic turned into book, go well down into the rabbit hole
> to see what it is all about.
"Convinced? BUY NOW! Unsure? DOWNLOAD FREE CHAPTER! Skeptical? Scroll
down..."
.
.
.
.
[Christ, how far down does this thing go?!]
.
.
.
[Still going...]
.
.
[Hey, I can see my house from here!]
.
.
"LAND of LISP: Secrets of the Seven Guilds."
Hey, you really *don't* need drawing skills to do a web comic!
Giant talking ants? What the... oh, I see what you did there. *sigh*
"Holy cow, I LOVE honey!"
WTF-O-Meter: 4.6
"Any humans foolish enough to resist with their primitive weapons were
dealt with in short order."
There goes Java, Python, C# and Ruby. (What, no C or C++?)
"There's a long-forgotten place where they have weapons SO POWERFUL that
they can defeat ANY BUG! They call it the Land of Lisp."
Uhuh. So an untyped language with a single global namespace and which
touts self-modifying code as its single most significant feature is the
way to beat program bugs?
O RLY?
"Greetings, your highness!"
Creatures with 12 eyes and an arm for a nose: WTF-O-Meter: 1.5
"SILENCE! Back in the eighties, we showed you how to program WITHOUT
HAVING ANY BUGS! We warned you what would happen, but you didn't listen
to us."
Uh, yeah, right. I'm pretty sure there's no programming language in
existence that completely prevents bugs. :-P
"Each of the Seven Guilds possesses a powerful bug-killing weapon that
is unique to Lisp!"
Oh yeah?
So macros, functional programming, software transactional memory,
restartable code and "conciseness" are unique only to Lisp?
I say again: O RLY?
"Functional programming is a mathematical approach to programming that
was pioneered by Lisp."
I beg to differ.
Actually, I'm not even sure why people consider Lisp to be a functional
programming language. JavaScript is about as functional as Lisp!
"How it kills bugs: Writing code in a functional style guarantees that
[...] This makes it very easy to debug."
And writing code in a language which *enforces* a functional style makes
it drastically easier to debug. :-P
(No need to worry about whether any of the libraries you're using took
shortcuts. No temptation to take shortcuts yourself.)
"True macros are one of Lisp's most unique and amazing features. True
macros allow you to add new functionality to Lisp in a very fundamental
way."
Such as...?
"Lisp macros are so powerful that you can write your own if-then command!
(defmacro three-way-if (expr a b &rest c)
(let ((val (gensym)))
`(let ((,val ,expr))
(cond ((and (numberp ,val) (zerop ,val)) a)
(,val ,@c)
(t ,b)))))
[...] you'll make your like much easier by writing a macro!"
Riiight. Because
if_then True x y = x
if_then False x y = y
is sooo much harder to implement, eh? Newsflash: lazy evaluation
eliminates one of the major reasons for wanting macros in the first place.
For the other reasons, we have Template Haskell. This allows you to
write Haskell at compile-time. (Lisp, AFAIK, doesn't have a
"compile-time", so things are slightly different.) And, unlike Lisp
macros (AFAIK), Template Haskell statically guarantees that the code
written by a template is *always* syntactically correct. (It does _not_
guarantee that it is well-typed, just that there are no syntax errors.)
Also unlike Lisp macros, a Haskell template can inspect and alter
anything in the current module (up to and including completely rewriting
it). Like Lisp macros, Haskell templates are written in vanilla Haskell.
"Using restarts and the Lisp REPL, a bug can be fixed in a running program."
While that /does/ sound pretty cool, it's only possible because Lisp is
interpreted and untyped. Also, they managed to give an example fragment
that is a perfect candidate for software transactions. That's cute.
(I am unable to determine enough about the semantics of restarts to
comment further.)
"To modify the value of a variable in Common Lisp, you use setf.
However, this command also has an amazing special power: Instead of a
variable name, you can pass it a complex Lisp expression that retrieves
a value."
Right. Because you could never implement something like this in any
other language...
"They're using this incredible device called a Wii. Say cadet, why
aren't you shooting anything?"
"I'm trying to, but the controller keeps thinking that I want to HUG the
insectiod storm-troopers!"
LOLrus.
"Those ships are from the DSL Guild."
Uhuh. Because no other language allows you to embed a DSL right into
your programming language. Or even, you know, embed a language with a
syntax entirely different from that of the host language. (Haskell's
"quasi-quoting" allows you to embed a DSL that gets read using a parser
that you write, using any parser libraries you desire.)
"Those ships are from the CLOS Guild."
I have no idea if this is good or not. Nor do Lispers, apparently.
"What the... Oh, I forgot about those obnoxious Schemers from the
Continuation Guild."
Again, because no other programming language has continuations, right?
"Basically, continuations let you put 'time travel' into your code."
Actually, you don't need continuations to do that, necessarily.
"Weaknesses: Continuations are such an awesome feature that they don't
really have a downside."
O RLY?
How about the ease with which you can make your program so complicated
that it becomes totally unmaintainable?
"Holy s**t! What are those?! Jeez! Those are not the kind of bugs we had
in the eighties! Wait... I can't believe it! The NEW Lisp guilds have
come to join the battle!"
OK...
"Those ships are form the Brevity Guild."
Yeah, true. There are no other programming languages that are brief,
right? (Scroll upwards for one tiny Haskell v Lisp example.)
"(accum a (for n 1 1000 (unless (some [is 0 (mod n _)] (range 2 (- n
1))) a.n)))"
That computes the prime numbers from 1 to 1000? OK, how about
primes = let f (p:xs) = f $ filter (\x -> x `mod` p > 0) xs in f [2..]
Yes, *clearly* only Lisp can be brief and unintelligible.
(Note well that the Haskell variant generates *all* the prime numbers in
the universe, not just the ones less than 1000.)
As an aside, does Lisp have arbitrary-precision arithmetic yet? Cos that
Haskell fragment gives you arbitrary-precision results. Using the GMP,
no less.
"Now that computers have multiple cores, there is a lot of interest in
finding elegant ways to use them. One popular approach is Software
Transactional Memory."
Wow, that sounds amazing! Yep, that's definitely a unique Lisp feature.
Oh, wait...
(Pity you can't guarantee that the code in your transactions has no
side-effects, eh?)
"And finally, the Lazy Guild."
Oh wow. This, truly, no other programming language has ever had...
"(take 20 (filter even? (iterate inc 0)))"
Or, to put it another way,
take 20 (filter even (iterate (+1) 0))
or even
take 20 $ filter even $ iterate (+1) 0
No bracket-counting. :-P
"Humanity had been saved! Programmers could go back to programming, no
longer in fear of bugs. By learning the lessons of the Ten Guilds of
Lisp, programs could become richer and more robust than ever before."
I realise that this is of course propaganda, and nobody is _seriously_
suggesting that Lisp is the silver bullet to end all programming
problems, but seriously. Any programmer who does not fear bugs either
doesn't care if his programs work or not, or is a fool.
Hmm, now here's a thought. What would the Seven Guilds of Haskell look
like? What are the top ten bug-killing features of the language?
1. The type system.
Haskellers have an expression: If it compiles, it usually does what it's
supposed to.
While I emphasize "usually" [i.e., not "always"], this expression exists
for a reason. It's almost creepy how often the type checker can figure
out that your code is broken without even running it. Yes, it's annoying
when the type checker stops your code from running when it's perfectly
"obvious" what it should do. But I lose count of the number of times
I've battled against the type system, only to eventually realise some
fatal flaw in the design of my program - all without ever having
actually run it! The type system forces you to *think* about what you're
doing, and often you figure out that you've missed an important
possibility because the types don't line up.
And then, yes, now and then you want to do something which actually *is*
perfectly safe, but the type system won't let you. It doesn't happen
very often though...
2. Functional purity.
The comic talks about time travel and how useful it is. Well, if your
data never changes, going backwards in time becomes a trivial operation.
(Going forwards, of course, is another matter...)
Go look at the interactive Huffman compressor. I initially wrote it
using destructive updates. But after battling with all sorts of obscure
bugs, I eventually came to realise that I wanted to keep different
copies of (say) the min-heap containing the symbol probabilities at
different stages of construction. And that's really, really hard if you
keep destructively updating it. If you look at the code I realised,
you'll find almost all the data is actually immutable.
(You may say that's because I've been brainwashed by Haskell and that
I'm incapable of programming any other way now. I would suggest that
this is not the case. Immutable data actually makes the code simpler to
write in this instance.)
The trouble with destructive updates is that you can easily forget that
you're doing them. Haskell, with its wonderful type system, *lets* you
perform destructive updates, but *reminds* you that you're doing them
with the type signatures.
3. ???
Haskell has many fantastic features. I'm not sure which ones I could
specifically point to as "bug killers" though.
Multi-core programming would probably be one to point at. Writing
multi-core programs is notoriously bug-prone. Where Smalltalk, Java,
Eiffel and so on give you threads and locks, Haskell (or more precisely,
the de facto Haskell implementation) give you half a dozen different
tools, all of which can be used simultaneously:
- OS threads.
- Lightweight threads.
- Sparks.
- Nested data parallelism.
- Locks.
- Transactions.
Lightweight threads scale to thousands or tens of thousands of threads
in a single program, without killing a modest laptop. You can use them
with locks, or you can use transactions (the Software Transactional
Memory mentioned by Lisp).
That handles /concurrency/ (i.e., doing several different things at
once). For /parallelism/ (i.e., doing the same thing with multiple
cores), we have sparks and data parallelism.
Sparks are trivially lightweight code annotations. You add one extra
function call, and it's parallel. No threads, no locks, no semaphores,
no race conditions, no memory leaks, nothing. The only possible hazard
is laziness. (I.e., your program might not do as much work in parallel
as you had intended.)
Nested data parallelism is even more mental. It's still in early
development, but the idea is that you specify a set of array
transformations, and the run-time engine figures out the best way to
spread the work across multiple cores, taking into account cache
coherence, load-balancing, and so forth. (And who says you need Matlab
to do that?)
Then there are tools like Thread Scope, which let you interactively
visualise your parallel [or serial] Haskell application's run-time
performance in real-time. (Currently only analysis thread performance
though. Older profiling tools handle space analysis and so on.)
But we're drifting away from bug-killing now.
Also related to bug-killing is Haskell's total absence of null-pointers,
and it's "typed unions". Slightly less related is the monadic action
notation, which allows you to do the kinds of time-travelling the Lisp
comic talks about, but with a syntax that's no different than ordinary
I/O operations.
(This of course is due to Haskell's hardwired "do-notation".
Unfortunately other sequencing primitives such as arrows get no such
benefit, sadly. I guess we need somebody to write a quasi-quoter or
something...)
> It's the brainchild of Conrad Barski, from lisperati. He also has something to
> say about haskell:
>
> http://lisperati.com/haskell/
Hoookay, let's take a look...
"The only preparation you need to do is to install the Glasgow Haskell
compiler - you can get the latest version from here."
Actually the preferred way to do this now is to install the Haskell
Platform. But anybody following the link will find that out, so no drama.
"Now you have all you need to run the Hello World program below."
16 lines of code for Hello World?
Oh, there goes half our audience...
Best of all, the only 2 lines of /executable/ code could actually be
trivially rewritten as just 1 line. But I'm presuming he's done it this
way because he intends to append to it. (?)
"runhaskell tutorial.hs"
Well, at least he did it the correct way (runhaskell, not runghc). He
could have told everyone to put a hash-bang at the top... >_<
"Every programmer should know about regular expressions."
O RLY?
Also, according to /my/ documentation, Text.Regex doesn't exist. You
have to import Text.Regex.Posix (or some other sub-module, depending on
exactly which regex variant you want - but only POSIX is provided by
default). Then again, we don't know what GHC version he tested against
(and therefore which version of the regex package).
"I'm not happy if I can't use 'comparing', and I think we all want this
to be a /happy/ tutorial."
WTF-O-Meter: 1.9
"The way Haskell compilers handle types puts them head and shoulders
above most other compilers."
I think you mean "the way the Haskell language handles types", but sure,
it's one of the major strong points of the language.
"The first few 'type' lines should be self-explanatory; a Point is just
a tuple of floating numbers, a a Polygon is just a list of Points"
Yeah, you neglected to mention that "[Int]" means "list of Int". :-P
The fact that (Float,Float) is a pair of Floats is fairly self-evident,
but [Point] being a list of points is decidedly /not/ obvious.
"For instance, the EnergyFunction takes an arbitrary type 'a'"
You haven't explained why "Float" is a concrete type, but "a" is an
arbitrary type. In fact, you haven't even explained that Haskell can
handle arbitrary types polymorphically.
Then again, this is a tutorial. Maybe he's expecting to go over this later.
"Just paste this new code to the button of the existing program."
Thought so.
I wonder how many people will notice the indentation? It's critical that
it matches the code above it, and this isn't very visually obvious. (!)
"
let people :: [Person]
people = read people_text
"
Or, as I like to put it,
let people = read people_text :: [Person]
"Something else that's really 'good' is that when we read our people
from the file, we didn't have to tell Haskell what type of data we were
reading."
Uh, looks to /me/ like you did, actually. :-P
Now, if you have arranged your example so that the compiler could
/infer/ the type, and didn't need a type signature, your statements
would seem far more persuasive.
"We're going to draw pictures in a really cool way: We're going to write
our own SVG files from scratch!"
I was waiting to see how a vanilla GHC install does graphics. I was
thinking maybe PPM, but SVG is a master-stroke... I'll have to remember
that one.
<big wedge of code>
OK, that's a pretty big wedge of code. In fact, it demonstrates a common
Haskell characteristic: it's a zillion miles wide, and only a few lines
long.
He's apparently defining every function in the program as a local
variable. That's very bad style, but presumably we wants to be able to
just concatenate each block of code to the end of the program.
Also, there's a HELL of a lot of concatMap calls in there. I doubt too
many people will figure out WTF that is all about.
Also, "zip.repeat"? I think you mean "zip . repeat". :-P And you're
going to have to explain a whole bunch about curried functions and
function composition before it makes any semblance of sense.
"Haskellers never use loops- Instead, they either use recursion to do
looping, or they use functions like map that take other functions as
parameters. Functions that do this are called higher order functions."
Well that's as clear as mud. (Functions that do "this"? Which "this" are
you referring to?) Sure, *I* know what he's talking about, but it's not
the best sentence structure ever.
"In this example, we're using a clever variant called concatMap that
also concatenates the result of the mapping together, saving us a step."
...which only makes sense once you know that the result of the mapping
step is required to be a list or string.
"If you can understand what colorize = zip.repeat means, you'll
understand probably most of what Haskell has to offer in just three words!"
Yeah, that's what I said a few paragraphs ago. :-P
"You've gotta admit, a language that can do that is pretty cool! Writing
functions with the "dangling values" removed is called writing in a
point-free style."
I'm sure at least a few people are at this point thinking "dude, WTF?
That's not *cool*, it's *confusing*!"
"Arguably, Haskell is the one language that has the toughest type
checking and allows for the briefest code- That means Haskell allows you
write code that's more bug free than almost any other language. I would
estimate that I spent only about 3% of my development time debugging
this program, which I could never accomplish that in another language.
(Now, mind you, it can be a b*** to get your program to compile properly
in Haskell in the first place, but once you get to that point, you're
home free :-)"
I think he likes it...
<another huge chunk of code>
Oh... oh my god. Now I see why he's using the regex package. He's taking
SVG as input, and using a regex to process it, rather than a real
parsing library. o_O
(Need I even begin to explain why using a regex to process XML is an
absurdly bad idea?)
Still, I guess it's better than explaining how to use Parsec [which is
also available out-of-the-box] to build a true parser. Properly parsing
XML is a large problem. Still, it would be nice to see him try...
"This code illustrates another situation where Haskell's laziness really
makes things easy for us: All the regular expression functions just take
regular 'ol text strings."
Yeah, lazy I/O can be great. It can also result in massive memory leaks
or files held open too long if you're not careful. Might be worth
mentioning that.
"Somewhere, deep in the brain of every programmer, is a neuron that has
only one role in life: Whenever a sound is recorded by the hair cells of
the ear that sounds like "Cartesian Product", this neuron will dump
unimaginable quantities of neurotransmitters at the nodes of other
neurons, that translate into the neuron-equivalent of "Oh my God!!
INEFFICIENCY ALARM RED!! All Neurons to full alert! RUN RUN RUN!!!"
Enough said."
LOLrus.
"What's Bad About this Code?
Well, these four functions are going to be the bread and butter of our
this program- Probably, about 99% of CPU time will be spent in this
little bit of code... Consequently, every little inefficiency in this
code will hurt performance hundredfold... And boy, is this code
inefficient."
"These things can be fixed relatively easily by ugli-fying the code"
Well there's a good advert for using Haskell then. >_<
"Of course, the total number of annealing steps we're doing (500) is not
enough for a very good annealing- You'd need to run a few million steps
and use GHC to compile the program to machine language to get an optimal
result- Here's how you'd compile it:
ghc -O2 -fglasgow-exts -optc-march=pentium4 -optc-O2 -optc-mfpmath=sse
-optc-msse2 --make picnic.hs"
Or, you know,
ghc -O2 --make picnic
which is going to produce near enough the same damned result. (Most
especially, -fglasgow-exts enables extra language features which the
tutorial DOES NOT USE. Adding this has NO EFFECT!) To make it go faster,
you should change the horrifyingly inefficient algorithms mentioned
above, not twiddle compiler switches.
"As this example shows, Haskell's powerful typing system allows us to
prevent leakage from different sections of code in ways almost no other
language can match."
The statement is true, but the example does not demonstrate this
particularly vividly.
"Despite its many advantages, I humbly suggest, therefore, that in the
future there will continue to be a rift between the "imperative" and
"functional" camps of programming, until someone comes up with a truly
robust way of uniting these two camps- And I think that some profound
programming discoveries still need to be made in the future before this
problem is really resolved- I get the feeling it's just not good enough
to wave at the problem and say "Monads"."
We shall see...
*I* would suggest that there are much more real problems preventing
widespread adoption of Haskell. For example, half of Hackage won't
compile on Windows. That's a pretty big show-stopper, right there.
Calling "floor" is 5x slower in Haskell than in C. That's a pretty big
show-stopper if you want numerical performance. Nothing to do with lofty
questions of theory, just dull old real-world concerns.
Random note: Take a look at this announcement from Debian.
http://www.debian.org/News/2010/20100806
The freeze notice babbles on about KDE, GNOME, OpenOffice, Apache, PHP,
Python, Ruby, GCC and... GHC? In the same sentence?
It appears people are starting to take notice...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
10 PRINT "BASIC RULES!"
20 GOTO 10
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible <voi### [at] dev null> wrote:
so friggin' big I thought it would be fair for the original author to take a
look, otherwise all criticism was in vain... so I mailed it to Conrad Barski...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
in any case...
Invisible <voi### [at] dev null> wrote:
> "LAND of LISP: Secrets of the Seven Guilds."
>
> Hey, you really *don't* need drawing skills to do a web comic!
you should know that by now, XKCD fan...
> "Any humans foolish enough to resist with their primitive weapons were
> dealt with in short order."
>
> There goes Java, Python, C# and Ruby. (What, no C or C++?)
I think it's implicit... ;)
> "There's a long-forgotten place where they have weapons SO POWERFUL that
> they can defeat ANY BUG! They call it the Land of Lisp."
>
> Uhuh. So an untyped language with a single global namespace and which
> touts self-modifying code as its single most significant feature is the
> way to beat program bugs?
if by global namespace you mean no modules/libs, you're very wrong.
Lisp is not untyped either, just leaves type-handling to runtime. :)
> "Greetings, your highness!"
>
> Creatures with 12 eyes and an arm for a nose: WTF-O-Meter: 1.5
your WTF-O-Meter should go up once you realize that is how he depicts Lispers...
which BTW is not really that far from reality... :)
> "SILENCE! Back in the eighties, we showed you how to program WITHOUT
> HAVING ANY BUGS! We warned you what would happen, but you didn't listen
> to us."
>
> Uh, yeah, right. I'm pretty sure there's no programming language in
> existence that completely prevents bugs. :-P
comic license... you did notice the title can be shortened as LOL, right?
> "Each of the Seven Guilds possesses a powerful bug-killing weapon that
> is unique to Lisp!"
>
> Oh yeah?
>
> So macros, functional programming, software transactional memory,
> restartable code and "conciseness" are unique only to Lisp?
all in the same line, yes. :)
well, not quite "conciseness" as Lisp languages tend to favor words and
long-descriptive names rather than single-char operator obfuscation. Unless
we're talking about big problems where DSL-building with macros give a clear
gain in readability can indeed bring conciseness...
> "Functional programming is a mathematical approach to programming that
> was pioneered by Lisp."
>
> I beg to differ.
you don't: in the 50's and 60's when Lisp was born there was no Haskell,
Miranda, ML nor even lowly C: FORTRAN or the soon to appear COBOL were your
only alternatives for high-level code... and those were definitely not
functional. Lisp was.
> Actually, I'm not even sure why people consider Lisp to be a functional
> programming language. JavaScript is about as functional as Lisp!
It's because any language with support for creating functions on the fly,
receiving functions as arguments to other functions and returning functions as
the result of evaluation of functions is able to use the functional programming
paradigm.
> And writing code in a language which *enforces* a functional style makes
> it drastically easier to debug. :-P
yes, and writing it in a lazy evaluation language makes it damn hard to debug as
you also well know.
> if_then True x y = x
> if_then False x y = y
>
> is sooo much harder to implement, eh? Newsflash: lazy evaluation
> eliminates one of the major reasons for wanting macros in the first place.
It's true that unevaluated argument-passing (call-by-name) is one of the fine
reasons for macros or, obviously, lazy evaluation. But I'd say it's a minor
bonus when compared to its main use to generate code at compile-time and build
custom syntax for the sake of readability.
> For the other reasons, we have Template Haskell. This allows you to
> write Haskell at compile-time.
> (Lisp, AFAIK, doesn't have a
> "compile-time", so things are slightly different.)
Pretty much all Common Lisp implementations are compilers, specially noted the
commercial Allegro CL and the open-source sbcl. In Scheme, there's gambit,
bigloo and a few other such batch-compilers. So, yeah, macros are expanded at
compile-time and the generated expressions are all eventually transformed down
to lambdas in continuation-passing-style which are them finally compiled to
either C or native code.
But even in implementations using JITs to compile to native-code, like racket,
there's a clear separation of phases, even though no executable is formally
created.
Now, given their dynamically-typed natures, they won't usually generate as fast
code as static type compilers, but hopefully not by much:
http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=sbcl&lang2=ghc
OTOH, when writing code for speed, you actually pin down types for variables
with (declare ...) compiler directives in the source code. CL allows it
intrusively, in the bodies of functions, but I prefer gambit scheme way, by
allowing such declares in the command line for the compiler. :)
In any case, it seems racket (previously PLT-Scheme) JIT kinda humiliates sbcl
old-way batch compiler without even needing to pin down types:
http://shootout.alioth.debian.org/u64/benchmark.php?test=all&lang=racket&lang2=ghc
> And, unlike Lisp
> macros (AFAIK), Template Haskell statically guarantees that the code
> written by a template is *always* syntactically correct. (It does _not_
> guarantee that it is well-typed, just that there are no syntax errors.)
that's good for a language heavy on syntax.
> Also unlike Lisp macros, a Haskell template can inspect and alter
> anything in the current module (up to and including completely rewriting
> it).
sounds as deadly as ninja patching. :)
other than that, I'm not sure what you mean by inspect and alter anything in the
current module...
do you have any practical example other than the short but confusing already
examples at:
http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/template-haskell.html
the examples there seem to deal with a problem that doesn't exist in Lisp in the
first place: generate code to deal with different types.
> Like Lisp macros, Haskell templates are written in vanilla Haskell.
>
> "Using restarts and the Lisp REPL, a bug can be fixed in a running program."
>
> While that /does/ sound pretty cool, it's only possible because Lisp is
> interpreted and untyped.
they compile a module and load it into the same executable image in order to run
in place of the older version.
> "They're using this incredible device called a Wii. Say cadet, why
> aren't you shooting anything?"
>
> "I'm trying to, but the controller keeps thinking that I want to HUG the
> insectiod storm-troopers!"
>
> LOLrus.
completely random Nintendo ad... :p
> "Those ships are from the DSL Guild."
>
> Uhuh. Because no other language allows you to embed a DSL right into
> your programming language. Or even, you know, embed a language with a
> syntax entirely different from that of the host language. (Haskell's
> "quasi-quoting" allows you to embed a DSL that gets read using a parser
> that you write, using any parser libraries you desire.)
yeah, but it seems so mindnumbing complex nobody uses.
BTW, you know quasi-quoting comes from Lisp, don't you? `(let ((r ,(fact 5)))
r) will turn into (let ((r 120)) r) at compile-time... it's so friggin' easy
it's used a lot.
> "Those ships are from the CLOS Guild."
>
> I have no idea if this is good or not. Nor do Lispers, apparently.
Common Lisp Object System is one of the main prides old Lispers enjoy chatting
about. It's the ultra-flexible polymorphic, late-binding object system that
comes with Common Lisp.
I never used it but can see it's value for OO folks.
> "What the... Oh, I forgot about those obnoxious Schemers from the
> Continuation Guild."
>
> Again, because no other programming language has continuations, right?
yes, this is actually an innacurate description of Scheme, but they got it right
that Lispers usually don't get along with schemers, even though it's all Lisp.
Scheme is also far more geared towards functional programming than CL, another
factual error there.
All programs have continuations, it's just that so few programming languages
offer them as first-class objects.
first-class continuations and the tail-call optimization used by all functional
programming languages were born or brought to light with Scheme as described in
the lambda-the-ultimate papers in the 70's:
http://library.readscheme.org/page1.html
> "Basically, continuations let you put 'time travel' into your code."
>
> Actually, you don't need continuations to do that, necessarily.
you may not need special syntax for it if you explicitly write your programs in
continuation-passing-style, in which case you get continuations for free. Of
course, that requires tail-call optimizations otherwise your stack blows...
> "Weaknesses: Continuations are such an awesome feature that they don't
> really have a downside."
>
> O RLY?
>
> How about the ease with which you can make your program so complicated
> that it becomes totally unmaintainable?
remember continuations are essentially gotos. ;)
you're taking a comic book too seriously...
> "Those ships are form the Brevity Guild."
>
> Yeah, true. There are no other programming languages that are brief,
> right? (Scroll upwards for one tiny Haskell v Lisp example.)
some people consider python a quite apt Lisp. Including Google's old Lisp
visionaire Peter Norvig.
> "(accum a (for n 1 1000 (unless (some [is 0 (mod n _)] (range 2 (- n
> 1))) a.n)))"
>
> That computes the prime numbers from 1 to 1000? OK, how about
>
> primes = let f (p:xs) = f $ filter (\x -> x `mod` p > 0) xs in f [2..]
>
> Yes, *clearly* only Lisp can be brief and unintelligible.
>
> (Note well that the Haskell variant generates *all* the prime numbers in
> the universe, not just the ones less than 1000.)
>
> As an aside, does Lisp have arbitrary-precision arithmetic yet? Cos that
> Haskell fragment gives you arbitrary-precision results. Using the GMP,
> no less.
arbitrary-precision arithmetic is what Lisp got since ancient times -- they call
it a full numeric tower, no overflow error. In fact, if you want performance
out of small toy examples, you should state something like fixnum... they also
predate GMP, but some newer implementations are using it for the sake of
maintenance...
> "(take 20 (filter even? (iterate inc 0)))"
>
> Or, to put it another way,
>
> take 20 (filter even (iterate (+1) 0))
>
> or even
>
> take 20 $ filter even $ iterate (+1) 0
nah, inc is 1 char shorter. ;)
whew, I'll call it a draw here and reply about the Seven Guilds of Haskell
later...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp <war### [at] tag povray org> wrote:
> 10 PRINT "BASIC RULES!"
> 20 GOTO 10
(let goto ()
(display "Scheme rulez! ")
(goto))
:)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
On 30/10/2010 05:04 AM, nemesis wrote:
> Warp<war### [at] tag povray org> wrote:
>> 10 PRINT "BASIC RULES!"
>> 20 GOTO 10
>
> (let goto ()
> (display "Scheme rulez! ")
> (goto))
fix (putStrLn "Haskell rules." >>)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
On 29/10/2010 08:35 PM, nemesis wrote:
> so friggin' big
That's what SHE said...
> I thought it would be fair for the original author to take a
> look, otherwise all criticism was in vain... so I mailed it to Conrad Barski...
Well... just so long as he knows I wasn't being entirely serious.
(Neither was the comic - presumably.)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
>> "There's a long-forgotten place where they have weapons SO POWERFUL that
>> they can defeat ANY BUG! They call it the Land of Lisp."
>>
>> Uhuh. So an untyped language with a single global namespace and which
>> touts self-modifying code as its single most significant feature is the
>> way to beat program bugs?
>
> if by global namespace you mean no modules/libs, you're very wrong.
Is it possible to create variables with local rather than global scope?
> Lisp is not untyped either, just leaves type-handling to runtime. :)
Strictly speaking, an /untyped/ language is one where you can apply any
operator to any data - no matter how stupid. (E.g., you can multiply two
strings together, or take the cosine of a pointer.) Very few real
programming languages fall into this category (although a few
intermediate compiler languages do).
The main distinction is between statically-typed and dynamically-typed
languages. A statically-typed language does extensive and usually
exhaustive testing in a fully automated way to statically guarantee that
your program cannot malfunction due to mismatched types. A
dynamically-typed language doesn't bother doing any compile-time
checking, and just lets your program crash at runtime if you make a mistake.
The advantages of static typing are obvious (i.e., vast swathes of bugs
are eliminated before you even *get* to run-time). Dynamic typing, on
the other hand, has only two tiny advantages: 1; You don't have to write
type signatures everywhere. 2; The compiler doesn't stop you performing
valid actions just because it upsets the type system.
Most statically-typed languages have a primitive, puny type system and
demand explicit type signatures everywhere. This makes the language
designer's job much easier, but isn't much fun for the programmer.
Coding in (say) Java, it's actually quite common to need to work around
the type system.
When you get to Haskell, of course, automatic type inference blows #1
out of the water, and the sophisticated Turing-complete type system more
or less kills #2 as well. If you really can't express what you want to
do easily, you can always make a small, carefully controlled hole in the
corner of the type system to get around that, and still keep the
overwhelming benefits of strong type-checking for the rest of the program.
Really, in the modern age, there is no reason for dynamically-typed
languages to exist at all. It just invites bugs.
>> "Greetings, your highness!"
>>
>> Creatures with 12 eyes and an arm for a nose: WTF-O-Meter: 1.5
>
> your WTF-O-Meter should go up once you realize that is how he depicts Lispers...
> which BTW is not really that far from reality... :)
I've always been puzzled by the assertion that Lisp is THE ultimate
solution to every programming problem. It seems to me to be more of a
religion than a rational argument. The exchange
"Why is Lisp the best?"
"Because it has macros!"
is as nonsensical as
"Why do you believe in God?"
"Because Christ died on the cross to save mankind from sin!"
It makes no sense.
>> "Each of the Seven Guilds possesses a powerful bug-killing weapon that
>> is unique to Lisp!"
>>
>> Oh yeah?
>>
>> So macros, functional programming, software transactional memory,
>> restartable code and "conciseness" are unique only to Lisp?
>
> all in the same line, yes. :)
I'm not sure how you came to that particular conclusion...
> well, not quite "conciseness" as Lisp languages tend to favor words and
> long-descriptive names rather than single-char operator obfuscation. Unless
> we're talking about big problems where DSL-building with macros give a clear
> gain in readability can indeed bring conciseness...
I would say that (0 - b + sqrt (b*b - 4*a*c))/(2*a) is significantly
clearer and more concise than (div (add (sub 0 b) (sqrt (sub (mul b b)
(mul (mul 4 a) c)))) (mul 2 a)). (Note that I had to get out a text
editor with syntax highlighting just to type all that with the correct
number of brackets!)
Yes, it's possible to overuse special characters as operators. But
generally a few carefully chosen operator names can greatly shorten the
code /and/ make it more readable.
>> "Functional programming is a mathematical approach to programming that
>> was pioneered by Lisp."
>>
>> I beg to differ.
>
> you don't: in the 50's and 60's when Lisp was born there was no Haskell,
> Miranda, ML nor even lowly C: FORTRAN or the soon to appear COBOL were your
> only alternatives for high-level code... and those were definitely not
> functional. Lisp was.
I still don't think Lisp is very functional. :-P
>> Actually, I'm not even sure why people consider Lisp to be a functional
>> programming language. JavaScript is about as functional as Lisp!
>
> It's because any language with support for creating functions on the fly,
> receiving functions as arguments to other functions and returning functions as
> the result of evaluation of functions is able to use the functional programming
> paradigm.
Depends on what you consider to be "functional programming". There are
really two separate characteristics that most FP languages have:
1. Pure functions.
2. Functions as first-class objects.
Any programming language that supports functions can support writing
pure functions. But as far as I can tell, Lisp does not in any way
encourage or facilitate doing so. Heck, C++ has facilities for marking
things that won't be altered after being initialised, whereas Lisp does
not. That makes C++ slightly more FP than Lisp.
>> And writing code in a language which *enforces* a functional style makes
>> it drastically easier to debug. :-P
>
> yes, and writing it in a lazy evaluation language makes it damn hard to debug as
> you also well know.
No, lacking a decent debugger makes debugging hard. Lazy evaluation
makes *performance* harder to predict/control, but it makes *debugging*
easier, since functions always produce repeatable results.
And, in fact, LoL touts lazy evaluation as an *advantage* just a few
points further down the list. :-P
>> Newsflash: lazy evaluation
>> eliminates one of the major reasons for wanting macros in the first place.
>
> It's true that unevaluated argument-passing (call-by-name) is one of the fine
> reasons for macros or, obviously, lazy evaluation. But I'd say it's a minor
> bonus when compared to its main use to generate code at compile-time and build
> custom syntax for the sake of readability.
You complain about "operator obfuscation" and then tout "custom syntax"
as improving readability. Which is it? :-P
>> (Lisp, AFAIK, doesn't have a
>> "compile-time", so things are slightly different.)
>
> Pretty much all Common Lisp implementations are compilers
Since Lisp explicitly permits self-modifying code, wouldn't that mean
that the compiler has to be present at run-time?
>> And, unlike Lisp
>> macros (AFAIK), Template Haskell statically guarantees that the code
>> written by a template is *always* syntactically correct. (It does _not_
>> guarantee that it is well-typed, just that there are no syntax errors.)
>
> that's good for a language heavy on syntax.
I won't claim to be a Lisp expert, but I was under the impression that
not every possible list is a valid executable Lisp expression.
>> Also unlike Lisp macros, a Haskell template can inspect and alter
>> anything in the current module (up to and including completely rewriting
>> it).
>
> sounds as deadly as ninja patching. :)
I have no idea what that is.
> other than that, I'm not sure what you mean by inspect and alter anything in the
> current module...
From what I can tell, Lisp macros work by you passing some data to
them, which they then transform into some sort of executable code.
Template Haskell allows you to inspect parts of the current module not
passed in as arguments. So you can, e.g., look up the definition of a
user-defined data structure.
> do you have any practical example other than the short but confusing already
> examples at:
No.
> the examples there seem to deal with a problem that doesn't exist in Lisp in the
> first place: generate code to deal with different types.
The examples demonstrate how to use TH, not why you'd /need/ TH. It's
perfectly possible to write highly polymorphic code with nothing but
plain vanilla Haskell '98.
>> "They're using this incredible device called a Wii. Say cadet, why
>> aren't you shooting anything?"
>>
>> "I'm trying to, but the controller keeps thinking that I want to HUG the
>> insectiod storm-troopers!"
>>
>> LOLrus.
>
> completely random Nintendo ad... :p
Or... anti-ad I suppose?
>> "Those ships are from the DSL Guild."
>>
>> Uhuh. Because no other language allows you to embed a DSL right into
>> your programming language. Or even, you know, embed a language with a
>> syntax entirely different from that of the host language. (Haskell's
>> "quasi-quoting" allows you to embed a DSL that gets read using a parser
>> that you write, using any parser libraries you desire.)
>
> yeah, but it seems so mindnumbing complex nobody uses.
I get the impression that people avoid it because it's not part of the
official language spec, it's GHC-specific. But mainly, because it's not
really needed especially often.
> BTW, you know quasi-quoting comes from Lisp, don't you? `(let ((r ,(fact 5)))
> r) will turn into (let ((r 120)) r) at compile-time... it's so friggin' easy
> it's used a lot.
I must be missing something... those two expressions look almost
identical to me.
>> "Basically, continuations let you put 'time travel' into your code."
>>
>> Actually, you don't need continuations to do that, necessarily.
>
> you may not need special syntax for it if you explicitly write your programs in
> continuation-passing-style, in which case you get continuations for free. Of
> course, that requires tail-call optimizations otherwise your stack blows...
Haskell provides a monad version of CPS, which means you don't even have
to obfuscate all your code to do crazy continuation manipulations.
Personally, this kind of stuff makes my mind melt. :-}
>> "Weaknesses: Continuations are such an awesome feature that they don't
>> really have a downside."
>>
>> O RLY?
>>
>> How about the ease with which you can make your program so complicated
>> that it becomes totally unmaintainable?
>
> remember continuations are essentially gotos. ;)
I think that says it all. :-)
> you're taking a comic book too seriously...
Perhaps. Maybe I'm just tired of Lisp fanboys claiming that this one
single language is some kind of perfect Utopia.
>> As an aside, does Lisp have arbitrary-precision arithmetic yet? Cos that
>> Haskell fragment gives you arbitrary-precision results. Using the GMP,
>> no less.
>
> arbitrary-precision arithmetic is what Lisp got since ancient times.
OK, fair enough.
>> "(take 20 (filter even? (iterate inc 0)))"
>>
>> Or, to put it another way,
>>
>> take 20 (filter even (iterate (+1) 0))
>>
>> or even
>>
>> take 20 $ filter even $ iterate (+1) 0
>
> nah, inc is 1 char shorter. ;)
And (+1) is more descriptive.
Also, I can soon change that to "iterate (*2) 1" to generate a geometric
sequence instead. I bet Lisp doesn't have a predefined "multiply by 2"
function. :-P
> whew, I'll call it a draw here
My point was that here the Lisp and Haskell versions are so utterly
similar that nobody can seriously claim that Lisp is more concise than
Haskell (or vice versa). So saying "only Lisp can be concise - look at
this!" isn't very compelling.
> and reply about the Seven Guilds of Haskell later...
Heh, yeah, I need to go think about that myself.
Actually, just the other day I was trying to pin down in my head exactly
which sorts of programs Haskell is good for and bad for.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible <voi### [at] dev null> wrote:
> Is it possible to create variables with local rather than global scope?
you're kidding me, right?
> (let ((x 1)) (+ 1 x))
=> 2
> x
reference to undefined identifier: x
BTW, let is mere syntatic sugar for function application:
((lambda (x) (+ 1 x)) 1)
> (let fact ((n 5)) (if (< n 2) 1 (* n (fact (- n 1)))))
=> 120
> fact
reference to undefined identifier: fact
fully lexically scoped.
> When you get to Haskell, of course, automatic type inference blows #1
> out of the water
yeah, except when it can't decide on the types all by itself and requests type
annotations anyway.
I would also say the Hindley-Milner kinda cheats in this regard. You usually
don't have to type annotate whenever the function operates on numbers, strings
or Algebraic Data Types. And Algebraic Datatypes are a dead on giveaway of the
type of an expression, because it usually have a name of its own to describe
values of its type. So, a function like:
withBuf run = run . Buf 0 ini =<< mallocBytes ini
where ini = 1024
needs no type annotation because it already has a friggin reference there to
type:
data Buf = Buf !Int !Int !(Ptr Word8)
so, you exchange type annotations everywhere for type names for values of this
type in the expressions. Not that much of a fair trade: instead of providing a
single type declaration, you provide it everywhere you use it.
I'm talking ill of Hindley-Milner type system, but you should be aware that the
same lameness can happen in Lisps too: here and now you will see things like
fl* fx+ or fl<... that is, functions to operate on specific numeric types.
Again, they exchange a single type declaration for type-specific operators
everywhere. Makes me wonder if the Algol folks got it right...
> Really, in the modern age, there is no reason for dynamically-typed
> languages to exist at all. It just invites bugs.
Dynamically typed languages are just perfect for quick prototyping of systems
where invariants are still not well known. The fact that some of them can not
just quickly prototype working systems, but prototype them with good performance
is only a plus.
> "Why is Lisp the best?"
> "Because it has macros!"
I almost never use macros and still find Lisp the best. :)
I suspect its homoiconic nature has lots to do with it. Plus, if you've never
edited Lisp code with hierarchical parenthetical editing you don't know what a
bless it is compared to any other language in existence.
> I would say that (0 - b + sqrt (b*b - 4*a*c))/(2*a) is significantly
> clearer and more concise than (div (add (sub 0 b) (sqrt (sub (mul b b)
> (mul (mul 4 a) c)))) (mul 2 a)). (Note that I had to get out a text
> editor with syntax highlighting just to type all that with the correct
> number of brackets!)
you don't need to type brackets with a proper Lisp editor such as emacs or
DrRacket. You usually just type alt+shift+( and it opens a pair and puts you
right in. You can select a big code section spanning several lines by just
alt+shift+(right or left).
> I still don't think Lisp is very functional. :-P
It's because you still don't know it well.
> Depends on what you consider to be "functional programming". There are
> really two separate characteristics that most FP languages have:
>
> 1. Pure functions.
> 2. Functions as first-class objects.
>
> Any programming language that supports functions can support writing
> pure functions. But as far as I can tell, Lisp does not in any way
> encourage or facilitate doing so. Heck, C++ has facilities for marking
> things that won't be altered after being initialised, whereas Lisp does
> not. That makes C++ slightly more FP than Lisp.
That's purely an implementation detail, not a language feature. Pure math-like
functions with no side-effects only depend on you writing side-effect-free code.
Would you really call C++ more FP than ML because nothing in the language spec
say "hey, this section is pure, you can optimize it away for concurrency"?
BTW, Haskell is the only such (well-known) language enforcing purity even for
IO.
> You complain about "operator obfuscation" and then tout "custom syntax"
> as improving readability. Which is it? :-P
new syntax doesn't mean &*%#$. macros have as much self-describing names as
functions.
> >> (Lisp, AFAIK, doesn't have a
> >> "compile-time", so things are slightly different.)
> >
> > Pretty much all Common Lisp implementations are compilers
>
> Since Lisp explicitly permits self-modifying code, wouldn't that mean
> that the compiler has to be present at run-time?
You got that "self-modifying code" all messed up. Lisp macros can operate on
Lisp code to produce new custom code, but that happens at compile-time.
If you really want it, you have eval at runtime, but it's as slow as everywhere
else.
> >> And, unlike Lisp
> >> macros (AFAIK), Template Haskell statically guarantees that the code
> >> written by a template is *always* syntactically correct. (It does _not_
> >> guarantee that it is well-typed, just that there are no syntax errors.)
> >
> > that's good for a language heavy on syntax.
>
> I won't claim to be a Lisp expert, but I was under the impression that
> not every possible list is a valid executable Lisp expression.
I mean there not much syntax in Lisp besides brackets, an expression evaluating
to a function (or macro) at the head and arguments for that function (or macro)
in the rest of that list.
> From what I can tell, Lisp macros work by you passing some data to
> them, which they then transform into some sort of executable code.
> Template Haskell allows you to inspect parts of the current module not
> passed in as arguments. So you can, e.g., look up the definition of a
> user-defined data structure.
that sounds as painfully side-effectful as when I first heard it. Hence Ninja
Patching. google it up...
> > BTW, you know quasi-quoting comes from Lisp, don't you? `(let ((r ,(fact 5)))
> > r) will turn into (let ((r 120)) r) at compile-time... it's so friggin' easy
> > it's used a lot.
>
> I must be missing something... those two expressions look almost
> identical to me.
yeah, you're missing that you write the first and the compiler turns it into the
second, at compile time: yes, it evaluates (fact 5) during compilation. It
would certainly not do any magic and evaluate (fact n) if n was not known at
compile time, but since it's a literal...
> > you're taking a comic book too seriously...
>
> Perhaps. Maybe I'm just tired of Lisp fanboys claiming that this one
> single language is some kind of perfect Utopia.
now, there are not that many Lisp fanboys out there, there are? Most I know are
more like Lisp fanoldmen...
Scheme doesn't have many fanboys either, more like irate compsci freshmen...
> >> "(take 20 (filter even? (iterate inc 0)))"
> >>
> >> Or, to put it another way,
> >>
> >> take 20 (filter even (iterate (+1) 0))
> >>
> >> or even
> >>
> >> take 20 $ filter even $ iterate (+1) 0
> >
> > nah, inc is 1 char shorter. ;)
>
> And (+1) is more descriptive.
not in Lisp where it looks like a silly function application... :p
> Also, I can soon change that to "iterate (*2) 1" to generate a geometric
> sequence instead. I bet Lisp doesn't have a predefined "multiply by 2"
> function. :-P
we could have something like:
(define (pf op x) (lambda (n) (op n x)))
(iterate (pf * 2) 0)
> Actually, just the other day I was trying to pin down in my head exactly
> which sorts of programs Haskell is good for and bad for.
math problems and formal proof automation would be my guess for best suited.
Real world is more complicated. say a script to automate a task. You don't
really need all that type theory to get it done...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |