POV-Ray : Newsgroups : povray.off-topic : My hypothesis Server Time
29 Jul 2024 16:29:14 EDT (-0400)
  My hypothesis (Message 1 to 10 of 54)  
Goto Latest 10 Messages Next 10 Messages >>>
From: Invisible
Subject: My hypothesis
Date: 2 Sep 2011 11:36:44
Message: <4e60f80c$1@news.povray.org>
Haskell has a reputation [among people who've actually *heard* of it] 
for being a bizarre, baffling and opaque language. Most people agree 
that no sane human could possibly comprehend something so utterly weird 
and confusing.

Which is strange, because to those of us that know it, Haskell is an 
especially /simple/ language, which very directly allows you to express 
what you want to say. Theories about its perceived "hardness" include 
the unusual syntax, the unfamiliar mode of operation, the lack of good 
documentation, the excessive amount of opaque mathematical jargon, or 
possibly the ludicrous number of monad tutorials. I know Warp's 
favourite theory is that people just don't "think functionally". But now 
I've come up with my own suggestion:

Hypothesis: The Haskell language isn't hard. Typical Haskell *programs* 
are hard.

Now what the heck do I mean by that? Well, to put it bluntly, Haskell is 
an extraordinarily powerful language, so people tend to use it to solve 
"hard" problems. It's not that there's anything hard about the Haskell 
language itself. It's just that people only use Haskell to solve 
problems that are hard to understand, using solutions that are even 
harder to understand. After all, the /really cool thing/ about Haskell 
is that you *can* solve hard problems with it.

To illustrate what I mean, consider a typical introductory Java 
textbook. Such a thing will contain chapters about how to set up some 
kind of Java compiler, the basic syntax rules of Java, how to create 
variables, how to write loops, and so forth. It will probably have a 
huge class reference at the back, because Java's standard class library 
is frankly rococo. Throughout the text, you'll find little (and not so 
little) code snippets which demonstrate one particular feature or idea. 
But what actual /example programs/ might you find?

Obviously this varies by author, but typical programs will usually include:

- Hello world. (As required by RFC 2100.)
- What is your name?
- Guess which random number I just picked.
- My command-line arguments are...
- This is how you read and write files.
- Open a window containing every widget type and print a message every 
time the user clicks something.
- A 4-function calculator. (Either CLI or GUI.)
- Tic Tac Toe.
- A simple client/server (so you can demonstrate network sockets).
- A simple program that runs in two threads.
- A simple program that runs in two threads and avoids race conditions.
- This is how you run SQL statements via ODBC.
- This is how you play media. (Sounds, pictures, video, etc.)
- One program that doesn't actually "do" anything, but implements an 
insanely complex class hierarchy just so the authors can demonstrate 
ever OO feature of Java ALL AT ONCE!
- Possibly a bad knock-off of MS Paint.

In other words, the example programs cover specific tasks line running 
SQL or loading graphics or making network connections, and then a few 
very simple things like Tic Tac Toe that take these elements and make a 
minimally "useful" program out of them. Hey, it /is/ an introductory 
book, right?

What might a typical introductory Haskell textbook contain? Well, there 
aren't actually many of those in print. There's a surprising number of 
books that assume that you /already know/ Haskell and use it as the 
vehicle for implementing stuff. But there aren't many books that aim to 
/teach/ you Haskell.

If you manage to find one, though, typical introductory programs might 
include:

- The factorial function, the Fibonacci numbers, Pascal's triangle, the 
prime numbers...
- A skeleton implementation of a self-balancing binary search tree.
- A small parser combinator library.
- A simple shell-style interpreter for the SKI combinator calculus.
- A domain-specific language for specifying search predicates.
- Hello World (in an advanced chapter, after you've finished learning 
the monadic identities).
- A simple web server (so we can show how easy concurrency is).
- An implementation of the Barnes-Hut N-body simulation algorithm (so we 
can show off Haskell's trippy parallelism support).
- A solution to the Santa Claus problem (showcasing Haskell's software 
transactional memory).

Now take a step back for a moment and just look at that list. These 
items are all very, *very* CS-heavy. I defy you to find me an 
introductory Java textbook that teaches you how to build a 
self-balancing binary search tree. Most Java programmers would retort 
"the library already *has* a perfectly good BST implementation; why 
write another?" Of course, writing a real, usable library isn't the 
point; /enlightenment/ is the point. The difference, it appears, is that 
Java programmers think that implementing a BST is pointless and silly, 
while Haskell programmers think this is "instructive" and "interesting".

In short, the languages make different assumptions. Java seems to think 
that you're either building enterprisey DB front-ends (either desktop 
applications, browser applets or server-side HTML generators) or 
scripting DVD menu pages. Haskell, on the other hand, seems to fully 
expect you to be designing a compiler or something.

In case you think my example page above is exaggerated, let's see what 
example code is contained in Real World Haskell, an actual book actually 
published by O'Reilly, an actual publisher. The book contains:

- A Haskell to JSON conversion library. (Includes a small 
text-formatting library as a component.)
- A function to translate glob patterns into (POSIX) regular expressions.
- A domain-specific language for expressing file search predicates, 
together with an engine that searches the file system for matches.
- Build a trivial parser monad and use it to parse the PGM file format. 
(I.e., write a program that loads PGM images.)
- A program that loads a PPM image of a barcode, and decodes the digits 
of the barcode. (EAN-13 barcodes only.)
- A program to parse /etc/passwd and allow interactive username lookups.
- Half a dozen small Parsec examples, parsing CSV files, JSON, HTTP 
headers, etc.
- A library implementing a bloom filter.

How many Java textbooks teach you how to read barcodes?


Post a reply to this message

From: Invisible
Subject: Re: My hypothesis
Date: 5 Sep 2011 04:52:52
Message: <4e648de4@news.povray.org>
On 02/09/2011 04:36 PM, Invisible wrote:

> Hypothesis: The Haskell language isn't hard. Typical Haskell *programs*
> are hard.

Let's test that hypothesis. Consider the following example:

   module Main where

   import System.IO
   import System.Random

   main = do
     x <- randomRIO (1, 100)
     main_loop x

   main_loop x = do
     putStr "Guess: "
     hFlush stdout
     l <- getLine
     let g = read l
     if g == x
       then putStrLn "Correct."
       else do
         if g < x
           then putStrLn "Too low."
           else putStrLn "Too high."
         main_loop x

This is written in Haskell. However, I would expect that anybody 
familiar with computer programming should be able to figure out what 
this does, without knowing anything at all about Haskell. I mean, you 
couldn't /write/ this code, but you can /read/ it and figure out what it 
does. Which would seem to strongly contradict "Haskell is incomprehensible".

...or maybe I'm just delusional from having spent too long with Haskell? 
Maybe it only looks readable to my eyes. What do we say, people?


Post a reply to this message

From: Warp
Subject: Re: My hypothesis
Date: 5 Sep 2011 12:01:36
Message: <4e64f25f@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> Let's test that hypothesis. Consider the following example:

>    module Main where

>    import System.IO
>    import System.Random

>    main = do
>      x <- randomRIO (1, 100)
>      main_loop x

>    main_loop x = do
>      putStr "Guess: "
>      hFlush stdout
>      l <- getLine
>      let g = read l
>      if g == x
>        then putStrLn "Correct."
>        else do
>          if g < x
>            then putStrLn "Too low."
>            else putStrLn "Too high."
>          main_loop x

  That doesn't look functional at all. It looks imperative.

  Throw in some functional tricks (such as infinite lists, currying and
such) and it becomes harder to figure out.

-- 
                                                          - Warp


Post a reply to this message

From: Alain
Subject: Re: My hypothesis
Date: 5 Sep 2011 12:46:40
Message: <4e64fcf0@news.povray.org>

> On 02/09/2011 04:36 PM, Invisible wrote:
>
>> Hypothesis: The Haskell language isn't hard. Typical Haskell *programs*
>> are hard.
>
> Let's test that hypothesis. Consider the following example:
>
> module Main where
>
> import System.IO
> import System.Random
>
> main = do
> x <- randomRIO (1, 100)
> main_loop x
>
> main_loop x = do
> putStr "Guess: "
> hFlush stdout
> l <- getLine
> let g = read l
> if g == x
> then putStrLn "Correct."
> else do
> if g < x
> then putStrLn "Too low."
> else putStrLn "Too high."
> main_loop x
>
> This is written in Haskell. However, I would expect that anybody
> familiar with computer programming should be able to figure out what
> this does, without knowing anything at all about Haskell. I mean, you
> couldn't /write/ this code, but you can /read/ it and figure out what it
> does. Which would seem to strongly contradict "Haskell is
> incomprehensible".
>
> ...or maybe I'm just delusional from having spent too long with Haskell?
> Maybe it only looks readable to my eyes. What do we say, people?

I think it's the first time I see actual Haskell code, and I can read 
that one easily.
But,then, it's a prety trivial example.


Alain


Post a reply to this message

From: Orchid XP v8
Subject: Re: My hypothesis
Date: 5 Sep 2011 14:17:26
Message: <4e651236$1@news.povray.org>
On 05/09/2011 09:52 AM, Invisible wrote:

> ...or maybe I'm just delusional from having spent too long with Haskell?

Trivial program... is trivial.

It seems everybody agrees on that. Now let's try the sort of code that 
typically appears in an introductory Haskell textbook:

   module Heap
       (
         MinHeap (), empty, is_empty,
         top, insert, delete_top,
         union, size, to_list, from_list, heap_sort
       )
     where

   data MinHeap x = Leaf | Node x (MinHeap x) (MinHeap x)

   empty :: MinHeap x
   empty = Leaf

   is_empty :: MinHeap x -> Bool
   is_empty Leaf = True
   is_empty _    = False

   top :: MinHeap x -> x
   top (Leaf      ) = error "empty heap"
   top (Node x _ _) = x

   insert :: Ord x => x -> MinHeap x -> MinHeap x
   insert x' (Leaf        ) = Node x' Leaf Leaf
   insert x' (Node x h0 h1) = Node (min x x') (insert (max x x') h1) h0

   delete_top :: Ord x => MinHeap x -> MinHeap x
   delete_top (Leaf        ) = error "empty heap"
   delete_top (Node _ h0 h1) = union h0 h1

   union :: Ord x => MinHeap x -> MinHeap x -> MinHeap x
   union h0 h1 =
     case (h0, h1) of
       (Leaf, _   ) -> h1
       (_   , Leaf) -> h0
       (_   , _   ) ->
         if top h0 < top h1
           then Node (top h0) h1 (delete_top h0)
           else Node (top h1) h0 (delete_top h1)

   size :: MinHeap x -> Int
   size (Leaf        ) = 0
   size (Node _ h0 h1) = 1 + size h0 + size h1

   to_list :: Ord x => MinHeap x -> [x]
   to_list (Leaf        ) = []
   to_list (Node x h0 h1) = x : to_list (union h0 h1)

   from_list :: Ord x => [x] -> MinHeap x
   from_list = foldr insert empty

   heap_sort :: Ord x => [x] -> [x]
   heap_sort = to_list . from_list

(This one - or something resembling it - appears in "The Fun of 
Programming", Jeremy Gibbons & Oege de Moor, Palgrave Macmillan, in 
chapter 1. Then again, strictly this isn't a book about learning 
Haskell. It's about functional programming in general - as best as I can 
tell...)

This is classic functional programming. The only thing missing is that 
there's no high-order functions or type-level programming. It's got just 
about everything else. Now let's see if Warp's claims hold up.

While the identifier names are strongly suggestive, I doubt anybody not 
mildly fuent in Haskell is going to figure out what's actually going on 
here. I'll post a Java translation in a minute. Then we can see whether 
the program becomes clearer (i.e., Haskell is the problem) or remains 
opaque (i.e., the algorithm is the problem).

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Orchid XP v8
Subject: Re: My hypothesis
Date: 5 Sep 2011 14:30:42
Message: <4e651552$1@news.povray.org>
On 05/09/2011 07:17 PM, Orchid XP v8 wrote:

> While the identifier names are strongly suggestive, I doubt anybody not
> mildly fuent in Haskell is going to figure out what's actually going on
> here. I'll post a Java translation in a minute. Then we can see whether
> the program becomes clearer (i.e., Haskell is the problem) or remains
> opaque (i.e., the algorithm is the problem).

OK, so attempting to compare like for like, here's the most "natural" 
way I could think of to implement this in Java:

   public abstract class MinHeap
   {
     public abstract bool is_empty();
     public abstract int top();
     public abstract MinHeap insert(int x);
     public abstract MinHeap delete_top();
     public abstract MinHeap union(MinHeap h);
     public abstract int size();
   }

   public class MinHeap_Emtpy extends MinHeap
   {
     private MinHeap_Empty() {}
     public static MinHeap_Empty empty = new MinHeap_Empty();

     public bool is_empty() {return true;}

     public int top() // Throw exception

     public MinHeap insert(int x)
     {return new MinHeap_Node(x, null, null);}

     public MinHeap delete_top() // Throw exception

     public MinHeap union(MinHeap h) {return h;}

     public int size() {return 0;}
   }

   public class MinHeap_Node extends MinHeap
   {
     public int datum;
     public MinHeap child0, child1;

     public bool is_empty() {return false;}
     public int top() {return this.datum;}

     public MinHeap insert(int x)
     {
       let h = this.datum;
       let l = x;
       if (h < l) {h = x; l = this.datum;}
       return new MinHeap_Node(l, this.child1.insert(h), this.child0);
     }

     public MinHeap delete_top() {return this.child0.union(this.child1);}

     public MinHeap union(MinHeap h)
     {
       if (h.is_empty()) return this;

       MinHeap_Node that = (MinHeap_Node)h;

       if (this.datum < that.datum)
       return new MinHeap_Node(this.datum, that, this.delete_top());
       return new MinHeap_Node(that.datum, this, that.delete_top());
     }

     public int size()
     {return 1 + this.child0.size() + this.child1.size();}
   }

Differences:
- I don't know how Java does generics, so this min-heap handles int only.
- I left out the list conversion stuff, because I'm not sure about 
Java's container types off the top of my head. The "idiomatic" way is 
probably to define an interator or something anyway...
- It's a while since I did Java. This might not actually compile for one 
reason or other. (In particular, I'm fuzzy on how auto-constructors work...)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Warp
Subject: Re: My hypothesis
Date: 5 Sep 2011 14:45:05
Message: <4e6518b1@news.povray.org>
Orchid XP v8 <voi### [at] devnull> wrote:
>    insert :: Ord x => x -> MinHeap x -> MinHeap x
>    insert x' (Leaf        ) = Node x' Leaf Leaf
>    insert x' (Node x h0 h1) = Node (min x x') (insert (max x x') h1) h0

  This is a good example of where it becomes confusing. Even after
studying it for a few minutes I can't really figure out what is it
that it's doing. (Or, more precisely *how* it's doing it. The 'insert'
name makes it obvious what is it that it does, but the code doesn't make
it at all clear how.)

  Maybe the problem is that in an imperative language the different
situations would be more explicitly expressed with an 'if...then...else'
block. Here it's not clear at all what is it that the code is expressing.

  Of course it doesn't help that the syntax is quite uncommon, and the
meaning of the different operators isn't clear. (For instance, it's unclear
whether the ' is some kind of operator, and if it is, what exactly its role
is. If it isn't an operator but somehow just part of the variable name, it's
highly unusual.)

  The lack of separators is also confusing to someone who is accustomed to
programming languages that use separators.

-- 
                                                          - Warp


Post a reply to this message

From: Orchid XP v8
Subject: Re: My hypothesis
Date: 5 Sep 2011 15:29:16
Message: <4e65230c@news.povray.org>
On 05/09/2011 07:45 PM, Warp wrote:
> Orchid XP v8<voi### [at] devnull>  wrote:
>>     insert :: Ord x =>  x ->  MinHeap x ->  MinHeap x
>>     insert x' (Leaf        ) = Node x' Leaf Leaf
>>     insert x' (Node x h0 h1) = Node (min x x') (insert (max x x') h1) h0
>
>    This is a good example of where it becomes confusing. Even after
> studying it for a few minutes I can't really figure out what is it
> that it's doing.

Hmm, OK. Apparently I've spent longer doing this than I thought...

Is the Java translation any easier to grok? Or is that equally baffling?

> (Or, more precisely *how* it's doing it. The 'insert'
> name makes it obvious what is it that it does, but the code doesn't make
> it at all clear how.)

Well, yeah, you can take a stab at identifier names in most languages.

>    Maybe the problem is that in an imperative language the different
> situations would be more explicitly expressed with an 'if...then...else'
> block. Here it's not clear at all what is it that the code is expressing.

I can rephrase it to

   insert x' h =
     case h of
       Leaf         -> Node x' Leaf Leaf
       Node x h0 h1 -> Node (min x x') (insert (max x x') h1) h0

if you prefer. I don't suppose that's any more enlightening though.

The irony is that "case" is a more powerful version of "if/then/else". 
(The latter is rigorously a special-case of the former.) Haskell's 
"case" is like what other languages have, on steriods. On the other 
hand, if/then/else is pretty self-explanatory, whereas a case-expression 
isn't quite so immediately obvious.

The fact that you can write [what appear to be] multiple definitions of 
the same function as a short-cut to a case-expression isn't the most 
intuitive thing. Until somebody tells you that's what it means.

>    Of course it doesn't help that the syntax is quite uncommon, and the
> meaning of the different operators isn't clear.

Sure. You can find strange operators in any language; I guess one of the 
biggest things about Haskell is that its syntax just plain /isn't like/ 
anything else.

C, C++, C#, Java, JavaScript, Pascal and a half-dozen other languages 
all have virtually identical function-call syntax, and (apart from 
Pascal) the rest of the basic guts of the language has mild syntactic 
differences. So if you know any of those languages, you can take a stab 
at reading any of the others. Haskell is utterly different. Doesn't even 
resemble anything else. (Except other obscure languages like ML.)

> (For instance, it's unclear
> whether the ' is some kind of operator, and if it is, what exactly its role
> is. If it isn't an operator but somehow just part of the variable name, it's
> highly unusual.)

It's not an operator, merely part of the variable name. It's a de facto 
Haskell idiom; if you have a variable named foo, the new version of it 
is named foo'. (If you have more than two versions, it's probably better 
to number them, although you do see people write foo'' and even foo'''.) 
Apparently it's supposed to look like the mathematical "prime" symbol 
(which is more usually used for derivatives, actually).

I'll grant you that one's /highly/ non-obvious. I could perhaps have 
called it y instead of x' for increased clarity. That's only one of your 
points, though.

>    The lack of separators is also confusing to someone who is accustomed to
> programming languages that use separators.

Sure. You do see a lot of people look at an expression like

   foo x y + z bar

and think to themselves "now what the hell does that mean?" Knowing the 
barest bit of Haskell, possible options include:

- foo(x, y + z, bar)
- foo(x, y) + z(bar)
- foo(x, y, +, z, bar)

It's also common for beginner's Haskell code to contain more brackets 
than a typical Lisp snippet because it's so disconcerting to figure out.



You apparently can't see far enough into the syntax to realise this, but 
I think perhaps part of the problem is the whole symmetry of it all.

The syntax for /inspecting/ data is identical to the syntax for 
/constructing/ data. Which makes the language very regular and 
beautiful, but it also perhaps makes it rather baffling to anybody 
trying to figure out whether you're inspecting or constructing stuff.

The syntax for a tuple /type/ is identical to the syntax for a tuple 
/value/. Compare:

   ('1', 2, 3.4, "five") :: (Char, Int, Double, String)

Thing on the left is data. Thing on the right is a type signature. Now 
consider

   (x, y, z) :: (x, y, z)

This perfectly valid Haskell expression has three value variables on the 
left, and (unrelated but identically named) three type variables on the 
right.

The exact same thing happens with list values and list types.

Here, the very symmetry of the language is arguably somewhat baffling. 
Patterns look like expressions. Types look like values. Once you know 
Haskell modestly well, you realise that patterns /only/ go in certain 
places, and everything else is an expression. Types /only/ go in certain 
places, so everything else is values. But until you reach that point, 
the language design certainly isn't helping you much.



PS. I just discovered that you can create local operator names in the 
same way that you create local variables. So you can make a function 
where the same operator name changes its meaning on each recursive call 
to the function. How evil is that?

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Darren New
Subject: Re: My hypothesis
Date: 5 Sep 2011 17:03:01
Message: <4e653905$1@news.povray.org>
On 9/5/2011 11:45, Warp wrote:
> Orchid XP v8<voi### [at] devnull>  wrote:
>>     insert :: Ord x =>  x ->  MinHeap x ->  MinHeap x
>>     insert x' (Leaf        ) = Node x' Leaf Leaf
>>     insert x' (Node x h0 h1) = Node (min x x') (insert (max x x') h1) h0
>
>    This is a good example of where it becomes confusing. Even after
> studying it for a few minutes I can't really figure out what is it
> that it's doing. (Or, more precisely *how* it's doing it. The 'insert'
> name makes it obvious what is it that it does, but the code doesn't make
> it at all clear how.)

I think part of it may be that you're thinking "insert" actually inserts 
something in the heap. Nope! "insert" is a function that when given a value 
and a heap, gives you back the new heap you would have were you to insert 
that given value into that given heap.

-- 
Darren New, San Diego CA, USA (PST)
   How come I never get only one kudo?


Post a reply to this message

From: Warp
Subject: Re: My hypothesis
Date: 5 Sep 2011 17:36:54
Message: <4e6540f6@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> On 9/5/2011 11:45, Warp wrote:
> > Orchid XP v8<voi### [at] devnull>  wrote:
> >>     insert :: Ord x =>  x ->  MinHeap x ->  MinHeap x
> >>     insert x' (Leaf        ) = Node x' Leaf Leaf
> >>     insert x' (Node x h0 h1) = Node (min x x') (insert (max x x') h1) h0
> >
> >    This is a good example of where it becomes confusing. Even after
> > studying it for a few minutes I can't really figure out what is it
> > that it's doing. (Or, more precisely *how* it's doing it. The 'insert'
> > name makes it obvious what is it that it does, but the code doesn't make
> > it at all clear how.)

> I think part of it may be that you're thinking "insert" actually inserts 
> something in the heap. Nope! "insert" is a function that when given a value 
> and a heap, gives you back the new heap you would have were you to insert 
> that given value into that given heap.

  What happens to the old heap?

-- 
                                                          - Warp


Post a reply to this message

Goto Latest 10 Messages Next 10 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.