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.
I know fuck all about Haskell, but that sounds vaguely like curried functions to me, which is the dumbest fucking idea to come out of functional programming ever.
I just posted this so I could bitch about curried function.
Thanks, that hint was enough to do it. I looked up "currying" on the wikipedia and now I'm beginning to understand. So in fact, the data type of Pt really is a -> (a -> Point a). And also, I don't have the necessary functional programming groundwork to read that tutorial. (But I will continue to try anyway.)
Perhaps you can enlighten me on why you believe curried functions to suck? It does not seem intrinsically obvious. What's more, Google returns no hits for "currying sucks" or "curried functions suck", so the rest of the Internet is no use in this matter.
I tried reading about currying a few weeks ago and my head imploded. That shit is weird. (But then, a lot of what falls under the "functional programming" heading seems weird to me: it just doesn't seem to fit my brain at all.)
I thought the wikipedia article was fairly easy to follow:
http://en.wikipedia.org/wiki/Currying
It's like, you want to have a function that takes two arguments, say, plus(2,4) = 6. But instead of doing it that way, you have plus(2) return a function that adds 2 to its argument, so you do (plus(2))(4) and that equals 6.
I guess you already have to be cool with the idea that functions can return functions. That was something I already understood from playing with Scheme. But other than that, currying seems a pretty straightforward concept.
What isn't straightforward is the pros and cons of currying in practice, which is why I'm interested in what >>2 has to say about the latter.
It's a retarded notation in my opinion, but it makes sense because of the way these languages don't take multiple arguments.
I usually just take a shortcut and interpret something like "a -> b -> c" as two arguments (a and b), and a result (c).
>>5
Take a look at http://haskell.org/hawiki/Currying
BTW, the "gentle introduction to Haskell" totally sucks. You should use Hal Daume's tutorial instead: http://www.isi.edu/~hdaume/htut/tutorial.pdf
> It's a retarded notation in my opinion, but it makes sense because of the way these languages don't take multiple arguments.
Well, that's just the thing, isn't it? Why are these languages arbitarily forbidding multiple arguments, and then going to great lengths to re-introduce them? You could argue that it's useful to be able to create functions that have several arguments set to static values, which is true, but currying sucks at this too, because you can't set arbitary arguments, you have to set them in a specific order. In the end, it's all incredibly lame academic wanking.
Actually, what it is, is an eruv. http://en.wikipedia.org/wiki/Eruv
Orthodox Judaism forbids a number of things on the shabbat. However, this is apparently far too inconvenient to actually follow, so what is done is that a wire is strung up around an entire neighbourhood, creating a "wall" that makes the whole neighbourhood a walled-in area, where these actions are allowed.
Basically, you create an arbitary limitations, and then you spend a whole lot of effort to get around your arbiatry self-imposed limitations without technically breaking it. I think it's retarded in religious dogma, and I think it's retarded in programming languages.
PS: Most modern functional/imperative hybrid scripting languages (Perl, Python, Javascript, and so on) can already do partial function evaluation in a much more generic and useful fashion. They could use some syntactic sugar for it, but it does work a lot better than currying.
It makes the semantics of the language much simpler for all functions to be equivalent; I think that's the real reason for it. You do have a point about not being able to partially evaluate in an arbitrary order, though.
Computer in this day and age are massively complex - why on earth would you want semantics to be simple to that extent?
No, I know, silly academic dogmatics.
Ah, a Perl programmer speaks.
>>11, but that is exactly why semantics should be simple. If the problem you're trying to solve is complex, why distract yourself by dealing with a big language as well?
Because I can solve a problem quicker if I don't have to fight the limitaitons of the language every step of the way?
For me, currying is nice because you can do things like map succ list
instead of map (\x -> succ x) list
. Or map (+1) list
as opposed to map (\x -> x+1) list
.
Simple constructions to build complex stuff versus complex constructions is just different philosophies in programming, I guess.
I don't see how the first example has anything to do with currying. Of course, I don't know the language, but I really don't see anything about that that couldn't be done just as well with syntactic sugar and no currying. For the second, are you currying infix operators? That seems questionable at best.
Overall, that's just the equivalent of syntactic sugar. Hardly a good argument for a fundamental construct.
And I realy disagree that you can dismiss things as "just" different philosophies. All philosophies are not born equal.
>>17
Well, the nice thing about it is that it doesn't need syntactic sugar for it to work (except for infix obviously).
A disadvantage with currying is that you can't have empty arguments or implied arguments or whatever they are called where you only have to enter the first argument and the second is implied. Haskell comes up with a contrived method to counter that.
As I said, most scripting languages can do that, without doing any currying. The syntax isn't as neat, but it does work a lot better than currying. In Javascript:
var partial=function(a,b) { some_function(1,a,2,b) }
partial(3,4);
Well, duh, C can do that. As can Haskell or pretty much anything else.
Yes, all the more reason to not bother with currying.
Of course, most of the scripting languages also do closures, so you can do:
var something=1,other=2;
var partial=function(a,b) { some_function(something,a,other,b) }
partial(3,4);
That doesn't need a closure.
/* C code */
int something=1, other=2;
void partial(int a, int b) {
some_function(something,a,other,b);
}
partial(3,4);
It does if you're changing the values of those variables.
That said, Javascripts closures act really strange. You often end up having to kludge them to make them work right.
>>24
Changing what variables?
There are only two variables in that example of yours, and I was referring to both of them.
Haskell doesn't forbid multiple arguments. It has two many ways to take multiple arguments. First is currying. The second is through tuples. Or you can make your own type like a tuple and have a function take that type.
(+) :: Int -> (Int -> Int)
This addition function takes an Int
which returns a function which takes an Int
which returns an Int
. I put the brackets in for illustration.
(+) :: (Int,Int) -> Int
This addition function takes a tuple of two Int
s and returns an Int
.
(+) :: (Pair Int Int) -> Int
This takes a type called Pair
which holds two Int
s and returns an Int
.
The second way is a lot like how C takes arguments. And Haskell can do what >>22 shows too, so currying is less a limitation than a feature or an integral part of the language because Haskell comes from typed lambda calculus.
I wasn't arguing against Haskell, just against currying. If Haskell can do all that, why does it bother with currying?
>>29
Your arguments were that it doesn't take multiple arguments and you can't set arbitrary arguments to static values. Haskell can take multiple arguments like I said in >>27. Haskell can do arbitrary arguments just like in >>20. Currying just forces setting the arguments in 1 order (but arbitrary is possible too like in >>20) which is convenient.
A tuple is a single argument.
I don't know about Haskell, but in Ocaml taking a tuple apart is hardly as nice as having variables handed to you directly. If you want to discuss syntactic ugliness to work around multiple arguments, this is it.
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.
It's convenient in many practical situations. End of story.
Wait, there are practical situations in which one uses Haskell?
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?
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?
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.
That's nice. I'm glad you have so much fun arguing with strawmen of your own invention.
age!
I'm >>1 and I'm still learning Haskell, just figured out monads the other day. This is pretty cool stuff.
>>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).
>>40
Yes I have. It is interesting that you claim to know what anonymous strangers on the internet have or have not figured out.
>>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.
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.
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.
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?
>>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.
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.
Who gives a shit about the theoretical background? Currying's useful.
>>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. :'(
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.
a half-assed tool for a half-assed job, i guess.
>>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.
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.)
Once again, have fun talking to yourself, !WAHa.06x36.
what
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
?
>>57
maybe something like: f _ _ 42 _ === (\a b c -> f a b 42 c)
>>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
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.
>>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)
>>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.
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.
>>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.
>>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. :)
> 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.
ごめん読めない
みなさん、外人さんなんですか?
僕は純粋な日本人です。
さぁ、何でそんな奴が此処に居るんだよ?とか思っていますよね?
それは僕には夢と希望と愛と絶望が織り交じった、特別な感動を味わうための
オミュニケート像が必要だからです。貴方達は今幸せですか?
もし幸せなら、こんな僕に何か教えていただけないでしょうか?
お返事待っております。
>>66
You have still not shown a single example where a cut-like syntax would work significantly better.
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.
>>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?
>>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?
...
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.
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.
(`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
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.
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.
>>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
That looks redundant to me. Why am I repeating myself?