Haskell confusion (81)

1 Name: #!usr/bin/anon 2005-11-05 03:09 ID:B+nCC/mJ

I'm trying to learn Haskell (for fun, of course). My only previous experience with the functional programming paradigm was playing around with Scheme a bit. I was nodding my head to "A Gentle Introduction to Haskell" until I got to this, from section 2.2, User-Defined Types:

"Note that the type of the binary data constructor Pt is a -> a -> Point a, ..."

This is from http://www.haskell.org/tutorial/goodies.html

Now, where the heck does this type come from, and what does it mean? Just a paragraph before, we learn that "-> is a type constructor: given two types t and u, t->u is the type of functions mapping elements of type t to elements of type u."

So Pt maps elements of "a -> a" to elements of "Point a"? Or does it map elements of "a" to elements of "a -> Point a"? How does either of these possibilities make any sense? Seems like it maps two elements of type "a" to a single element of type "Point a", no?

I tried the "just ignore and keep reading, hoping it will make sense later" technique, but the next section, 2.2.1, Recursive Types, defines the Branch type in a similarly baffling way. Hopefully, someone more knowledgeable here will be so kind as to clear this up for me.

32 Name: !WAHa.06x36 2005-11-16 12:44 ID:pwaUKK3K

>>30

Again, I was arguing about functional languages in general and the idea that taking multiple arguments is somehow bad, not Haskell in particular.

But let's talk about Haskell: As you say, you have not one but two different methods now to work around the one-argument-per-function limitation, currying and passing tuples. Why would you still want to hang on to the single-argument dogma? It's already created extra complexity and confusion by introducing multiple workarounds, instead of a single, well-defined method for taking multiple arguments.

33 Name: #!usr/bin/anon 2005-11-17 04:25 ID:Heaven

It's convenient in many practical situations. End of story.

34 Name: #!usr/bin/anon 2005-11-17 11:57 ID:Heaven

Wait, there are practical situations in which one uses Haskell?

35 Name: !WAHa.06x36 2005-11-17 12:52 ID:Heaven

>>33

Then why not implement syntactic sugar for a system that is more convenient, and doesn't have the limitaiton of only letting you partially evaluate only the final arguments?

36 Name: #!usr/bin/anon 2005-11-18 17:58 ID:Heaven

Um. There is a system that doesn't have that limitation. It's called tuples.

C has the ++ operator. It's limited, but convenient. When it doesn't suffice, you use += or +. Same idea, no?

37 Name: !WAHa.06x36 2005-11-18 19:46 ID:Heaven

Well, that just reduces currying to syntactic sugar, again. I'm quite sure any number of computer scientists and language designers would disagree with classifying it as that.

38 Name: #!usr/bin/anon 2005-11-18 23:10 ID:Heaven

That's nice. I'm glad you have so much fun arguing with strawmen of your own invention.

39 Name: #!/usr/bin/anon 2006-01-31 17:24 ID:B+nCC/mJ

age!

I'm >>1 and I'm still learning Haskell, just figured out monads the other day. This is pretty cool stuff.

40 Name: #!/usr/bin/anonymous : 2006-03-28 10:22 ID:zOHco2oP

>>37
Haven't you learned anything? Syntactic sugar is good. Say that a couple of times. Or would you rather index your C array with *(base + index)? Verbose syntax, as we've seen with Javur, is the very enemy of refactoring. And you aren't going to chisel your code onto slabs of marble the first time around. "The program is complete, now it only needs to be compiled."

Non-PITA partial application is the very reason that you can define things such as the following:
sum = foldr (+) 0
It follows, then, that
concat = foldr (++) []
Do that in LISP and choke on your tens of lambda expressions and lack of static typing a couple of years on when something unexpected comes along.

... also there's functions like 'flip', which is effectively (\f a b -> f b a). Though you are correct in that more useful orderings of parameters do come along. IMO that just goes to show that functional programming doesn't let you skip careful design either.

>>39
No you haven't. Monadic I/O maybe. That's not exactly hard, once you come to terms with tail recursion :-)

For the real ultimate power, read a good monad tutorial (Monads for the working haskell programmer, I think it's called), take two aspirin and prepare for a great big mindfuck. And another when you look up what a monad is in category theory (optional).

41 Name: !WAHa.06x36 : 2006-03-28 12:23 ID:SS6VpCbA

>>40

I'm a Perl programmer. I don't think you'll find me arguing against syntactic sugar. I was merely arguing that currying is not the quite all it's cracked up to be, and in practice is just an insufficiently convenient form of syntactic sugar. Note >>35.

42 Name: #!/usr/bin/anonymous : 2006-03-28 23:54 ID:Heaven

>>40
Yes I have. It is interesting that you claim to know what anonymous strangers on the internet have or have not figured out.

43 Name: #!/usr/bin/anonymous : 2006-03-29 16:13 ID:Heaven

>>42
Heh. Sorry about that. I'm used to a far lesser breed of people who hang out on programming boards. The "what's the printf command in C++ do???" kind. So I kind of assumed that like a true Internet hero, you'd read the IO monad part of the so-called "gentle introduction", grabbed a tight hold of "a two parts hubris, two parts misunderstanding and one part knowledge" and ran to where your parents wouldn't hear.

Mea culpa.

>>41
(Oh, friction between Haskell and Perl people. Who'da thunk it!) Certainly curried functions aren't without their issues, yet their immediate benefits in "real world code" as I've seen are so significant as to make me wonder if your distaste was really rooted somewhere else than mostly effortless partial application. Which is kind of odd, seeing as Perl is the vaguely C-like language where parentheses around function parameters are explicitly optional. I dare you however to present me with a day-to-day programming langage construct that was not without issues.

44 Name: !WAHa.06x36 : 2006-03-30 15:28 ID:SS6VpCbA

>>43

My main point is this: Curried functions were introduced because functional programmers somehow got into their heads that functions only take a single argument. Since nobody can write code that way, a workaround was needed, which is curried functions. The fact that you can make partially applied functions with it is a side-effect, and not the original purpose. It is, however, the only useful aspect of curried functions. So far, so good, but since this was not initially introduced as a way to partially evaluate functions, it's not very good at doing that.

What I'm saying is: Drop the idiocy of having functions that only take one argument, and also drop curried functions, and instead introduce a proper method to make paritally-applied functions that can actually apply any argument, and not just the last ones. Currying is just a workaround for a self-imposed limitation.

45 Name: #!/usr/bin/anonymous : 2006-03-31 04:47 ID:mdP3ZjKE

>>44

You're full of shit. Currying is one of the best things about haskell. It's all through the type-system, and it's used all through the standard library and common idioms of the language.

As for the 'single argument' thing, you can always make a function take a tuple of args. The only thing this succeeds in doing is making your code less flexible and annoying, so no one does it.

46 Name: dmpk2k!hinhT6kz2E : 2006-03-31 11:19 ID:Heaven

ITT we get along.

> Currying is one of the best things about haskell.

Currying struck me as something of interest mainly to mathematicians. I can't see how it would make Haskell great, or any other functional language for that matter.

Could you elaborate?

47 Name: #!/usr/bin/anonymous : 2006-03-31 15:15 ID:jLQEuxPf

>>46
It allows you to create partially-evaluated functions very, very easily, which comes in quite useful. You really need to try it before you'll get it, though. Lots of Haskell probably seems silly and useless from a distance.

48 Name: !WAHa.06x36 : 2006-03-31 18:56 ID:fwONFEhQ

>>45

You pretty obviously didn't read what I was actually saying, and neither do you seem to be aware of the theoretical background that the concept of currying comes from. Also:

> As for the 'single argument' thing, you can always make a function take a tuple of args. The only thing this succeeds in doing is making your code less flexible and annoying, so no one does it.

This is very much part of my argument.

49 Name: #!/usr/bin/anonymous : 2006-04-01 02:07 ID:Heaven

Who gives a shit about the theoretical background? Currying's useful.

50 Name: dmpk2k!hinhT6kz2E : 2006-04-01 02:46 ID:Heaven

>>47
I have tried currying, in Ocaml mind you, but I can't say I found any use for it.

Partial evaluation is handy, because I can pass around a single partially-evaluated function rather than all the arguments it took. I have difficulty seeing how passing around a function that took a single argument is helpful though. If you're going to do that, why not just pass around the argument in question instead?

Someone will have to explain this to me. :'(

51 Name: !WAHa.06x36 : 2006-04-01 14:56 ID:Heaven

>>49

Not useful ENOUGH. Because you can only partially evaluate the last arguments. From a practical viewpoint, this is a completely arbitary and meaningless restriction. From a theoretical viewpoint, it's a direct consequence of currying not being a method for partially evaluating functions in the first place.

In the end, it's half-assed.

52 Name: #!/usr/bin/anonymous : 2006-04-01 15:07 ID:Heaven

a half-assed tool for a half-assed job, i guess.

53 Name: #!/usr/bin/anonymous : 2006-04-01 16:19 ID:jLQEuxPf

>>51
That's why there's flip (flip f x y = f y x). Speaking of which, see what a nice definition that is? And flip foo does exactly what you expect.

If you have a function with four parameters and you want to fill in the third one, you could resort to a lambda, or write your own combinator like flip, but I don't see why that comes up enough to be a big deal. It's an issue of design: you put the most general parameters first.

54 Name: !WAHa.06x36 : 2006-04-02 14:04 ID:Heaven

>>53

So why not make a decent method for partial evaluation right from the start, instead of having several different ones, and various kludges?

(Answer, once again: Because currying isn't a method for partial evaluation, it's theoretical CS wanking. It's not meant to be useful, that's just a side effect.)

55 Name: #!/usr/bin/anonymous : 2006-04-02 14:38 ID:Heaven

Once again, have fun talking to yourself, !WAHa.06x36.

56 Name: !WAHa.06x36 : 2006-04-02 21:18 ID:Heaven

>>55

what

57 Name: #!/usr/bin/anonymous : 2006-04-04 00:35 ID:jLQEuxPf

OK, so let's talk about this, >>54. You want a better method for partially evaluating functions. Maybe something like f:4=foo for a function that's like f, but it doesn't take the fourth argument of f, and uses foo for that instead. So basically, assuming f takes 5 arguments, it would be shorthand for (\a b c d -> f a b c foo d).

Is that the sort of thing you had in mind? If not, what would be a better way to do it? Are there any languages out there that implement a partial-evaluation method you find satisfactory? (Perl doesn't, to my knowledge, offer anything even as succinct as the lambda version, but my knowledge of Perl is limited.) Could you name some non-contrived situations in which the above sort of syntactic sugar offers significant advantage over currying and the use of combinators like flip?

58 Name: #!/usr/bin/anonymous : 2006-04-04 01:51 ID:/nIL5qky

>>57
maybe something like: f _ _ 42 _ === (\a b c -> f a b 42 c)

59 Name: #!/usr/bin/anonymous : 2006-04-04 11:52 ID:OPMPwlS+

>>57
It's pretty trivial to use named arguments in perl if you simply suck @_ (the arguments list) into a hash; from there it's likewise pretty trivial to conditionally do stuff or not based on the presence of a given key in said hash. I don't know if this is quite what you're talking about. A quick example might be a socket wrapper that takes an address and an optional port (a fairly common scenario):

sub contrived_socket_wrapper {

my (%args) = @_;
die "contrived_socket_wrapper needs an address" if not $args{'address'};
$args{'port'} = 23 if not $args{'port'};
#... connect to host $args{'address'} on port $args{'port'}...

}

This can then be called like:

contrived_socket_wrapper (address=>'localhost'); # uses default port
contrived_socket_wrapper (address=>'localhost', port=>45); # uses port 45

60 Name: !WAHa.06x36 : 2006-04-04 11:54 ID:SS6VpCbA

>>57-58

That is pretty much what I meant. The obvious advantage over currying is that is one construct instead of several, and not a kludge developed from a silly theoretical concept.

Perl provides exactly the lambda method - my $partial=sub { func(1,2,$_[0],3,$_[1]) } - but that's not all that pretty in any language.

61 Name: #!/usr/bin/anonymous : 2006-04-05 02:24 ID:P9zRrO27

>>59
you should use a hashref for named arguments so that you can get "Odd number of hash elements" warning at compile time (because you're creating an anonymous hash in the call) instead of runtime (creating a hash from an array in the function)

62 Name: #!/usr/bin/anonymous : 2006-04-05 15:49 ID:jLQEuxPf

>>60
I'm still not clear on why you think currying is a kludge. And, again, I would appreciate a real-world example of where these other methods are beneficial.

You might like Scheme's cut, which is somewhat similar to >>58. It's defined in SRFI 26, http://srfi.schemers.org/srfi-26/srfi-26.html.

(cut f <> <> 4 <>) = (lambda (a b c) (f a b 4 c))

I prefer currying, since it handles the common case nicely. I should also add, Haskell has "sections", where you partially-evaluate binary operators: (/2) is a function that divides its argument by two. You can also use this with any function, like

(`f`2)

to give f a second argument of 2.

63 Name: !WAHa.06x36 : 2006-04-05 22:43 ID:Heaven

>>62

I suspect it's only the common case because you design with currying in mind in the first place.

I don't think currying is a kludge. I think functions like flip are kludges to work around the fact that currying is not as general as it should be.

And once again, do you know the actual theoretical background for currying? My main point is that currying was never designed to be useful - it was designed as a theoretical tool in lambda calculus. The fact that it is useful is a side effect, but there's no real reason to use it instead of a more general and more useful model for partial evaluation, except for the underlying theory, which is of no practical consequence.

64 Name: #!/usr/bin/anonymous : 2006-04-06 01:34 ID:jLQEuxPf

>>63
Yes, I certainly do design with currying in mind. I don't see what's wrong with that. Having a convention for the order of arguments is a good thing in any language, and putting the more general arguments first is as useful a convention as any.

flip is useful in other situations besides currying. Are you familiar with foldl? It folds up a list using a binary operator and a start value: foldl op start [a,b,c] is equivalent to ((start op a) op b) op c. You can for example flip the : operator (which adds an element onto the start of a list) and reverse a list with reverse ls = foldl (flip (:)) [] ls.

Alternately, because of currying, you can just define that as reverse = foldl (flip (:)) []. This is called pointfree style and is considered cleaner than the other definition, since it's done at a higher level, dealing with functions rather than their arguments. I don't think you could do that as cleanly without currying.

Again, why currying, or any technique I use for that matter, was invented is not important to me. If it's useful, it's useful. Maybe you don't think it's useful and we can argue about that, but it still doesn't matter why it was invented.

65 Name: #!/usr/bin/anonymous : 2006-04-06 10:28 ID:Heaven

>>61
Thanks. I thought I remembered seeing it done using a hashref, but was typing without a reference (lol), couldn't think why a hashref would be preferable, so went the easy route. :)

66 Name: !WAHa.06x36 : 2006-04-06 11:15 ID:SS6VpCbA

> Again, why currying, or any technique I use for that matter, was invented is not important to me. If it's useful, it's useful. Maybe you don't think it's useful and we can argue about that, but it still doesn't matter why it was invented.

Once again, I do think it's useful - but not as useful as it could be. And I think the reason that there isn't a more useful construct is an insistance on the part of the language designers to stick close to theoretical concepts instead of practical considerations, and that is really what I oppose.

The (or probably, one) theoretical foundation of functional programming is lambda calculus. The theoretical foundation of imperative programming is Turing machines. Yet while imperative languages don't feel the need to base all of their constructs on concepts of Turing machines, functional langauges can't see to shake the influence of lambda calculus on their designs. Much to their detriment, I would say.

67 Name: 名無しのオプ : 2006-04-06 12:24 ID:EORrmXTf

ごめん読めない

68 Name: 名無しのオプ : 2006-04-06 12:27 ID:EORrmXTf

みなさん、外人さんなんですか?
僕は純粋な日本人です。
さぁ、何でそんな奴が此処に居るんだよ?とか思っていますよね?
それは僕には夢と希望と愛と絶望が織り交じった、特別な感動を味わうための
オミュニケート像が必要だからです。貴方達は今幸せですか?
もし幸せなら、こんな僕に何か教えていただけないでしょうか?
お返事待っております。

69 Name: #!/usr/bin/anonymous : 2006-04-06 16:25 ID:Heaven

>>66
You have still not shown a single example where a cut-like syntax would work significantly better.

70 Name: !WAHa.06x36 : 2006-04-06 17:38 ID:Heaven

>>69

Your request makes no sense. One is pretty much a strict superset of the other - it can do everything currying can, but it can also do other things besides.

71 Name: #!/usr/bin/anonymous : 2006-04-06 22:59 ID:Heaven

>>70
So give me a practical application of one of these "other things." And if it can do everything currying can, how does it let you program in a pointfree style?

72 Name: #!/usr/bin/anonymous : 2006-04-06 23:02 ID:Heaven

>>70
So give me a practical application of one of these "other things." And if it can do everything currying can, how does it let you program in a pointfree style?

73 Name: !WAHa.06x36 : 2006-04-06 23:18 ID:Heaven

>>71

...

Ok. It lets you take a function like func(a,b,c) and partially evaluate argument a, leaving arguments b and c free.

I would have, you know, thought this was kind of obvious at this point? I dunno.

I have no idea what "pointfree style" means. It sounds like something very specific to Haskell, and I am not in any way arguing specifically about just Haskell here.

74 Name: #!/usr/bin/anonymous : 2006-04-07 13:30 ID:Heaven

>>73
That's exactly what currying does. I'm asking for a real-world example of one of the "other things" you speak of.

I explained pointfree style in >>64. Don't tell me you pulled a Slashdot and replied without actually reading the post.

75 Name: !WAHa.06x36 : 2006-04-07 14:05 ID:Heaven

>>74

Ok, I messed up the order of the arguments. Haven't used this stuff in ages. Better point: it won't let me partially evaluate argument b.

You explained nothing I could comprehend in >>64. I'm not fluent in Haskell syntax, and the only thing I see is that "pointfree style" means not writing "ls". I don't know what that means, so it tells me nothing whatsoever.

76 Name: #!/usr/bin/anonymous : 2006-04-08 01:20 ID:Heaven

>>75

(`func`b)

will do that, or (flip func) b. (That is still not a real-world example -- you haven't explained why you'll want to do func a too, and if you don't then you can just reverse the first two arguments.)

reverse ls = foldl (flip (:)) [] ls

The above defines reverse to be a function taking an argument ls, returning foldl (flip (:)) [] ls. In the following definition:

reverse = foldl (flip (:)) []

you've done the same thing, but without explicitly naming and passing off an argument. Cleaner and more high-level, and especially useful when you're defining a function as the composition of other functions. Section 8.3 of "A Gentle Introduction to Haskell" has a good example of this: http://haskell.org/tutorial/stdclasses.html

77 Name: !WAHa.06x36 : 2006-04-08 11:41 ID:Heaven

>>76

I refuse to believe you can't imagine a function where you might want to partially evaluate one or another argument. But OK, here's a mathematical expample: If you have a function integrate(f,x1,x2) that numerically integrates a function f from x1 to x2, you might define one function as the intergral of another from 0 to x. You might also, however, need a function that integrates any function from 0 to 2PI.

What does (`func`b) do, exactly? That's some particularly opaque syntax. As for flip, I already said that was a kludge to work around the limitations of currying (at least here - in the later example it serves a more useful purpose, but that's not really relevant here), and it's hardly very readable.

And the pointfree style thing is merely an implementation issue. Since we're not talking about a particular implementation, that's a fairly meaningless question.

78 Name: !WAHa.06x36 : 2006-04-08 22:04 ID:Heaven

>>76

Furthermore: >>58 and >>62 gave suggestions for implementations that both, it seems to me, support "pointfree style".

Yet further, I'd have to take issue with the claim that "pointfree style" is in any way clearer. By removing variable names in the function specification, you're making the code a lot less self-documenting, because it is no longer obvious at a glance how many and what kind of arguments the function accepts. You now have to examine and understand the function body to figure out the arguments. I can't really see how this is "clearer" in any other way than using less characters.

79 Name: #!/usr/bin/anonymous : 2006-04-22 10:37 ID:DarU6rvQ

>>78
And that is why it is generally considered a good idea to provide an explicit type declaration for all exported symbols in a Haskell module. Abuse of point-free style one component of the Haskell equivalent of spaghetti code.

Anyway, the real value of point-free code is that there are fewer bits that you need to remember, or look at, that way. This makes sense if you consider that most haskellers can remember map, foldl, etc basic stuff like a perl programmer knows his regexps. (I'm sure a cognitive psychologist could make some obsure point here about humans having a short term memory limit of 7 "things".)

Which definition of strUpper would you consider clearer?

strUpper str = map (\x -> toUpper x) str

or

strUpper = map toUpper

The first has seven words (plus the arrow and lambda-backslash symbols), apart from the equals sign which tends to disappear anyway, vs only three for the second.

80 Name: #!/usr/bin/anonymous : 2006-04-22 12:17 ID:Heaven

>>79

I don't know Haskell syntax, but I'd find

strUpper str = map toUpper str

the most readable.

81 Name: #!/usr/bin/anonymous : 2006-04-28 23:35 ID:Heaven

>>80
That looks redundant to me. Why am I repeating myself?

This thread has been closed. You cannot post in this thread any longer.