Hi, While currying is inherited from nice academic languages such as Haskell and OCaml, they have never gotten much traction in systems languages. I believe the reason is that there’s a distaste for “magic”, in other words – transformations of code that leads to unknown amounts of extra assembly instructions and/or memory allocations. For instance, in my field – it is okay to allocate memory, but if you do it takes quite a bit of setup (you need to specify alignment, which memory pool it is in, etc.).
I can see why curried syntax is nice in a theoretical sense (and admittedly also some practical sense), but as you have observed, it has real associated costs. My opinion is that in a systems programming language, that you should not support currying syntax. For instance, closures needs memory allocation (whether it is stack or heap), and therefore the language needs to have hooks in place so that the developer can specify where the allocation should take place. If given `add : int -> int -> int`, and you write a term `add x` and nothing more, you have left out some important information - where do you store the captured x? Even if it was a constant, where do you store `add 1`? Before you say “the stack”, remember – the stack isn’t free either, especially in systems with thousands of light-weight threads. My 2 cents? Just say no to currying! Thanks, PKE From: bitc-dev [mailto:[email protected]] On Behalf Of Jonathan S. Shapiro Sent: Thursday, February 12, 2015 10:33 AM To: Discussions about the BitC language Subject: Re: [bitc-dev] current status of currying On Thu, Feb 12, 2015 at 8:08 AM, Keean Schupke <[email protected]<mailto:[email protected]>> wrote: On 12 Feb 2015 15:54, "Jonathan S. Shapiro" <[email protected]<mailto:[email protected]>> wrote: Primitive functions use tuples for their arity like: primitive_add_int32 :: (int32, int32) -> int32 These get wrapped into an overloaded type class, but we can omit that detail and have simply: (+) a b = primitive_add (a, b) I think that the root of our disconnect is a little clearer now. What you suggest above is very similar to what F# does, and it is certainly workable. It has the very real advantage that arity is being explicitly encoded in the tuple syntax, which is also a simplifying thing. One effect of this in F# is that programmers seem to get divided into two camps. One camp comes from the functional programming world. That camp is comfortable with the curried application syntax, and will use it preferentially. The other camp comes from the world of C. That camp will also gravitate toward what is familiar, which is to say the tuple syntax. The two groups will tend to build libraries in mutually incompatible style, which raises a concern that the user base will ultimately bifurcate. One purpose of the present discussion about arity-aware function types was to see if we could satisfy the needs of both groups with a single surface syntax. Just incidentally, the tuple approach violates your view that types and implementation should be kept separate. While the tuple remains a product type here, the rationale for this design choice is that the compiler is going to treat "function accepting tuple" as a distinct implementation case. And note, *not* "function being *passed* a tuple. that is: f( : 'a -> 'b) (1, 'c') is not called using the native calling convention merely because the argument type is a tuple, but (g : ('a, 'b) -> 'c) (1, 'c') IS called using the native convention.[*] [*] Another option is to decide the calling convention after specialization, in which case f actually *would* get called using the native convention. Anyway, the purpose of the current discussion was to see if we might be able to successfully cary arity *explicitly* on the function type without breaking the type system. Given that we lose most general types in the process, I'm concluding that the answer is "no", and that we will probably need to adopt the tuple story. I don't see any reason the compiler to not provide the curried versions automatically. I'm not entirely sure what you mean by this. Do you mean that given def f (x, y) = ... the compiler will automatically know what to do with an expression f 1 ? That would be equivalent to inserting an automatic coercion, which will play havoc with type inference. Do you instead mean that in your example above the + method is curried while the primitive_add function is not? Do you perhaps mean a third thing? The compiler uncurrying pass knows the original primitive type (or imported function type) and it would be a compile error if we cannot sufficiently uncurry. If we proceed on the assumption that uncurrying never increases allocations (which may not be right), then uncurrying can only make things better. But the problem here is the adoption of curried syntax in the first place. The problem with curried syntax is that it places the programmer in the habit of preferring an idiom that (by default) *inserts* non-obvious allocations and then applying best-effort to get rid of them again. Both parts of that statement are bad for systems codes. shap
_______________________________________________ bitc-dev mailing list [email protected] http://www.coyotos.org/mailman/listinfo/bitc-dev
