Qqwy

Qqwy

TypeCheck Core Team

Currying: Elixir library to add currying capability to all your Elixir functions

Today I realized that it would be possible to implement currying-capability in Elixir, using some clever anonymous function creation. (‘continuation-style currying’).

There was already a library called curry, which required you to define your to-be-curried functions using a special macro. And it would then define 255(the max. arity in Elixir) different function heads for it.

Currying does not do that. Instead, when yo u call curry, the passed function is wrapped in an anonymous function accepting a single parameter. This anonymous function is constructed in a clever way that will re-construct a new anonymous function each time an argument is passed, until the original function’s arity is reached, in which case the original function is called and the result returned.

There’s also some niceties like curry_many which allows you to pass in a list of arguments to apply to a curried function at the same time, and an optional implementation of ~> as infix-variant of curry/2.

An example:

      iex> use Currying
      iex> enum_map = curry(&Enum.map/2)
      iex> partially_applied_enum_map = enum_map.([1,2,3])
      iex> results = partially_applied_enum_map.(fn x -> x*x end)
      [1,4,9]    

See the Currying library/hex package here!


I’d be very grateful for any feedback ;-).

Most Liked

Qqwy

Qqwy

TypeCheck Core Team

I completely agree with @benwilson512 on this. Elixir has made the choice of taking the first parameter as ‘most important’ parameter for its functions. The BEAM does not have built-in support for currying. The library I made here is indeed slower than not using currying, as anonymous functions are being built in intermediate steps.

Elixir is Elixir because of the design choices that were made (Both in itself, and also partially the things that the BEAM or Erlang decided for it). Elixir is awesome because of its ability to build on top of what’s already built in the past on the BEAM.

Elixir is a dynamically typed language with awesome ideas on macro-creation, improving documentation and testing, and promoting explicitness in general (basically, the dynamic types are the only things that aren’t explicit in Elixir).

I really like Elixir because of these things. There are other languages which I like for different reasons. I like Haskell because of its built-in currying capabilities, and its purity which allows for clearer pattern-matching/guards than in Elixir. I dislike Haskell for the ease at which one can write intelligible programs, and for its high learning curve because of some other design decisions (many of which exist for historical reasons).

I like Ruby because it lets you quickly prototype things. I dislike Ruby for its implicitness and approach to monkey-patching.

I like Prolog because it uses the concept of unification and logic programming. I dislike Prolog because it is very hard to wrap your mind around this difference, and because it is very hard to write non-trivial programs with it.

I like Idris because it is a dependently-typed functional language that lets you reason about your code, instead of testing it. I dislike Idris because it still is very new and badly documented and also has a very high learning curve for newcomers.

I like Inform 7 because it lets you write programs in the English language, like they are books. I dislike Inform 7 because you can only use it to write Interactive Fiction with it.

These are just some languages I’ve experienced over the years. There are many more. I would love a language that would perfectly fit my own needs, but the only way to get such a language is to create it myself. Also, these needs will change over time.

And finally, there is no single language that is best for all tasks. It is easier to formulate some tasks in some languages, but there is no one-language-beats-all.

I’m not entirely sure how I ended up here, but anyway, that was my rant for the day, or something :sweat_smile:

NobbZ

NobbZ

Currying plus partial application is just awesome, and after having used some haskell you do miss it really hard everywhere else.

Since I haven’t take a look into @Qqwy’s currying package so far, I will give the examples in haskell.

Given the funtion foldr defined as this:

foldr f z []     = z 
foldr f z (x:xs) = f x (foldr f z xs) 

now your job is to implement map on top of this:

You can use either the very naïv approach to just write it down as this:

map f xs = foldr (\y ys -> f x : ys) [] xs

This is a fully applied function but can be further reduced (eta-reduced to be specific) to the following:

map f = foldr (\x xs -> f x : xs) []

After one gets used to it, it is just cool, but maybe, if you do not know it already, you will probably never miss it :wink:

Something in Elixir we could do with currying might look like this (untested):

use Currying

def const(a, _b), do: a

curried_const = curry(&const/2)
Enum.map([1,2,3,4,5,6,7,8,9,10], curried_const.(5))

# without curried:
Enum.map([1,2,3,4,5,6,7,8,9,10], &const(5, &1))

I have to admit, in elixir it looks quite a lot more clear and readable, even more idiomatic to simply use a capture :wink:

I think it is nice to play with currying and partial application in elixir, but I might probably stick with pipes and captures, since they are a native language thing, while the curried stuff feels foreign.

themarlzy

themarlzy

For the ignorant, what’s the point of currying functions? What’s a use case that it solves for?

OvermindDL1

OvermindDL1

Yeah I was thinking of the ‘most changing argument being in first position in Elixir’ too, it is the one big big thing that has bugged me from coming from Erlang, the most changing argument SHOULD be in the last position.

However, the curry could always build things in reverse maybe? Or just special code to put the last argument as the first in the actual call?

EDIT: And on a side note, the most changing argument in last position is a BEAM thing too, the BEAM can optimize function calls that have matching head arguments better than it can matching tails (if the head differs then it has to rebuild the entire argument list instead of being able to re-use the start, yes it is different from how Lists work).

globalkeith

globalkeith

Interesting stuff QQwy.

I’m a huge fan of FantasyLand and I’ve been trying hard to learn this stuff.

Is it just me, or does Elixir have its map arguments the wrong way round, much like the underscore.js library did, which Brian Lonsdorf gently pointed out in his talk http://functionaltalks.org/2013/05/27/brian-lonsdorf-hey-underscore-youre-doing-it-wrong/

Edit: Sorry OvermindDL1, looks like you already made that point!

Where Next?

Popular in Libraries Top

blatyo
https://www.conduitframework.com/ The best overview for how things are tied together is this presentation. Modules and functions are pre...
New
Qqwy
Solution is a library to help you with working with ok/error-tuples in case and with-expressions by exposing special matching macros, as ...
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
marcuslankenau
I feel kind of stuck with the absence of a proper xml library for Elixir. Currently I use SweetXML which was ok for me more or less to pa...
New
mindok
What is ContEx? A pure Elixir server-side data plotting/charting library outputting SVG. It has nice barcharts in particular and works g...
New
hpopp
After just over two years in development, this latest version of Pigeon is what I finally consider done in regards to my original vision ...
New
kelvinst
There was a feature proposal recently on Elixir core mailing list where an idea born. I got very interested in the idea, but it is not go...
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: https://github.com/tmbb/phoenix_ws Phoenix channels are a great...
New

Other popular topics Top

Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Sub Categories:

We're in Beta

About us Mission Statement