Haskell has a way of making one feel dumb.  This is by far the most
challenging programming language I've ever used.

i wouldn't see either as a particularly positive attribute. a language should 
enable,
explain, encourage, and perhaps inspire. if it also leaves you feeling that you 
could
do more and go further should you ever want or need to, that's okay, too.

but (unless designed for such purpose;) a language should not be a puzzle, nor
an obstacle, nor should its users feel judged or needlessly challenged.

http://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html

of course, all of that also depends on not only on the language, but on how it is used, presented, and perceived. haskell allows for a wide variety of approaches to programming, allowing programmers to be productive in plain old functional programming, or perhaps seemingly less so in advanced pointless type-level meta-programming. that doesn't mean that either approach is better or worse than the other, or that one approach fits all problems, or programmers. even one's measures of what constitutes simple code can evolve over time.

so, by picking those techniques that work for you, and keeping an eye open for
those that you're not (yet) comfortable with, you seem to be on the right track.

perhaps other techniques will make more sense to you later, or you'll find the
need to overcome limitations in the techniques you use now (which is how most
of the more complicated-looking themes have developed from easier-looking
ones in the past), perhaps not. understanding more advanced haskell tricks can
be fun, but for most programmers, the point is not (just:) to engage in 
intellectual
games and challenges for their own sake, but in exploring what their current
understanding of haskell does or does not allow them to do.

I won't try to understand fix just yet, but I'm still confused by the type of 
fix:

    fix :: (a -> a) -> a

It appears to me that it takes a function as an argument, and that
function takes a single argument.  So how are you passing fix an
anonymous function taking 2 arguments?  Sorry if I have beaten this
horse to death, but my pea-sized brain is working overtime here.

fix takes a function as an argument, and that function takes a single argument.
that function also returns something of the same type as its single argument.
which is where things get more interesting/complicated. but first:

lets give the argument of fix a name and its type: f :: a -> a

so fix takes f and produces an a, but where can that a come from?

that a could be the result of f, but then we'd need something of type a to put
into f. so, once again, we need an a. where could this a come from (i'm not a
language, so i feel free to pose challenging puzzles;-)?

hint: the relevant equation for fix is

    fix f = f (fix f)    -- (1)

read from right to left, we can see that f applied to (fix f) returns (fix f), which is why (fix f) is called a fixed point of f (and fix the fixed-point combinator).

read from left to right, we can see how fix accomplishes the feat of computing
a fixed point for any function f :: a -> a we might pass in as a parameter - it applies f to the fixed point yet to be computed, which unfolds to:

   fix f = f (f (..... f (fix f)..)..)

so, f gets applied to a copy of itself, applied to a copy of itself, applied to 
..
in other words, fix f recursively creates a supply of copies of f, which f can
make use of in its definition. lets say f = \self-> .. self .., then

fix (\self-> .. self ..) = (\self-> .. self ..) (fix (\self-> .. self ..)) -- by (1)
   = .. (fix (\self-> .. self ..)) ..     -- reduce and substitute for self
   = .. ((\self-> .. self ..) (fix (\self-> .. self ..))) ..     -- by (1)
so, fix f supplies f with copies to use for recursive calls within its body, and that a type is really the type of the body of f, as well as that of the self-reference.

which brings us to that extra challenge of extra parameters. so far, f is a 
function
only so that its body can take in a self-reference, and fix f corresponds to a recursive variable definition. if we want to define a recursive function instead,
we need more parameters. lets say f = \self-> \x-> .. (self x) .., then

fix (\self->\x-> .. (self x) ..) = (\self->\x-> .. (self x) ..) (fix (\self->\x-> .. (self x) ..)) = \x->.. ((fix (\self->\x-> .. (self x)..)) x) .. = \x->.. (((\self->\x-> .. (self x)..) (fix (\self->\x-> .. (self x) ..))) x)..
(it really helps to perform the reductions yourself, rather than read them, btw)

and what has happened to the type of f? f takes a self-reference, and returns a
function of one argument, so
   f :: ?? -> (b->c)

and the type of the self-reference is the type of f's body, so

   f :: (b->c) -> (b->c)

or

   f :: (b->c) -> b -> c

from which we can see that the type of fix gets instantiated to

   fix :: ((b -> c) -> (b -> c)) -> (b -> c)

or
   fix :: ((b -> c) -> (b -> c)) -> b -> c

and suddenly, fix does have two parameters, which flip can flip!-)

no magic, just technology sufficiently advanced to be indistinguishable from it:
a function of one parameter, which returns a function of one parameter, is a
function of more than one parameter.

at which point this particular fixed-point combinator puts its recursive unfoldings to rest for tonight.

hth,
claus

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to