let curry: Currying functions with named arguments #2102
Replies: 1 comment 1 reply
-
Hey there, thanks for writing such a detailed proposal! When thinking about possible changes or additions to a language (this is applicable beyond just Gleam) it's important to keep a few things in mind:
I think this proposal misses out on a few of these and suffers for it, and instead of leaving you in the dark I'd like to explain why.
This proposal jumps straight into semantics without explaining what problem we're trying to solve here or what kind of things are made possible with this proposal. Now I know @lpil has experience with languages with auto-curried functions like Elm or Haskell as do I, and clearly you do too, but I would wager that the majority of Gleam devs are coming from languages where function currying is not built into the language or particularly idiomatic. It would be super helpful for everyone if we can see what kinds of Gleam code are made possible/better/nicer/etc by this proposal.
I actually wrote a proposal a few months back that exists in the same sort of problem space as this proposal. I'm not here to shill my own ideas (there's a reason it went nowhere!) but I would like to direct you to this other proposal that spawned out of the discussion that is also proposing a way to introduce currying into Gleam and it would be great to hear how your proposal differs/expands/compliments/etc compared to that one.
Here is unfortunately where I feel this proposal is let down most. Gleam has a handful of design goals, some explicit and some unstated (and so it's not reasonable for you to have known):
The language pretty much ignores all the "fancy" features from other statically typed functional languages (traits, row polymorphism, module functors, GADTs, ...) in pursuit of a design that is easy to grasp and be productive with. I have quite a lot of experience with a number of functional languages and this snippet...
... left me scratching my head for a while. Upon multiple reads I am honestly still quite unsure what is happening here. Of course "does Hayleigh understand it" is a pretty rubbish metric with which to measure a proposal's quality but I am concerned with how others that are unfamiliar with currying may understand (or not) this feature. Additionally, I find this idea particularly concerning:
Much of Gleam's design is done in a way that helps those coming from non-functional languages (or perhaps functional-adjacent languages) like JavaScript or Rust feel comfortable and productive as quickly as possible. One of the most exciting additions (for me) to the language was the Anecdotally, I have seen this happen with Elm. A language that is otherwise very similar to Gleam in terms of semantics but one made the choice to a) adopt auto function currying and b) use a Haskell-like syntax. I know plenty of people that were turned off by the language despite being otherwise enthusiastic/interested because these two things become a significant barrier for them.
This one is not stated anywhere explicitly, so you can't be reasonably expected to know, but Gleam has little-to-no runtime component by design. We use what the target platform(s) give us and that's it. This makes targeting new languages in the future trivial (Adding the JavaScript target did not take much effort at all, for example) but it also makes the language and compiler much simpler. I mentioned this snippet previously, and I'd like to bring it up again:
The implication here is now every construction of a curried function now also comes with it a noticeable runtime cost of carrying around the original function and all supplied arguments. This would be a significant departure from the current language as there is no {retro/intro}spection anywhere else in the language[1]. Given the number of discussions you've started recently, it's clear you're enthusiastic about the language and want to help make it better. The number one thing you can do to help Gleam along at the moment is make things with it! (And then ideally tell us about the things you've made so we can see ^.^) I hope this doesn't come off as too rude or discouraging. It's great that you're thinking about how to improve the language 💖. [1] you might argue that |
Beta Was this translation helpful? Give feedback.
-
THIS IS NOT A JOKE!
Hi @lpil,
I think I have another syntax proposal :) I'm sorry if there were too many of them lately. I hope they somehow
benefit you as well, not only myself.
It is about named arguments and currying functions. Gleam, in this regard, has support for labeled arguments. But, for the sake of our experiment , let's assume they don't exist in gleam, and we are free to repurpose them for whatever we want. I will refer to them as named arguments.
Suppose we have a function:
It has 3 named args,
a
,b
,c
. What does that mean? Well, in our world, these are the arguments, that can be bound to a function, thus creating a partial function:Here we bind
a
to 1 andb
to 2. We could bind all three of them as well. In this case:Mixing regular and named arguments is forbidden. However, you can mix the (named) bindings:
A partial function knows its metadata. We can get it by doing something like:
The use of partial functions is not new to functional languages. However, here comes the main thing: let curry.
We can get the bound values back from a partial function by doing this:
What is going on here?
a
andb
are the args we boundf
to, previously. But what isg
? It it a function of two arguments.g(a, b)
gives us backf
. Well, actually, it gives a new function, but it's equivalent to f.Currying is like the inverse operation of binding to a variable. We can get the bound variable back, and rebind the function to something else:
Curried function
g
is still a partial function, bound to other arguments. In the case above, it can be bound tob
and/orc
. Therefore, you can do the currying multiple times:Now I hope you can see why I called it curry.
Why would you need this at all? The answer is code reuse. It is often needed to bind a function to a value, and it's as often needed to rebind it.
Now, the important aspect is wrapping functions. Functions are wrapped all the time in functional programming. Shall we lose the metadata of the bound functions, and will therefore unable to do the currying?
Not necessarily. First of all, here is how we can do the wrapping:
The wrapper here has the same (or bigger) set of the named args as the wrapped function does. And the first thing that is does is the binding of the latter. This way, the currying of the wrapper will work ok.
However, a better way would be to provide a special syntax for wrapping:
Here,
f
is bound automatically to all of its named arguments, and the compiler can check that the names of the named args coincide. It's much less error-prone, isn't it?In cases where you want the wrapper and the wrapped fun to have different sets of named args, you are kind of on your own. However, probably, the
wraps
syntax will help you in this case as well (because you can have intermediary functions one wrapping another).Does that make sense? What do you think?
Beta Was this translation helpful? Give feedback.
All reactions