massimo

massimo

I have implemented the Roc lang `<-` operator using macros

I was recently made aware of a new programming language, the Roc language that is still in early development stage, but looks very interesting on many aspects, especially the fact that it took a lot of ideas from Elixir.

In particular it uses the <- operator as syntax sugar over async operations, for example

a <- File.read(filename)

is automatically tranlsated to

a = Task.async(fn -> File.reade(filename) end) |> Task.await()

and I was wondering if I could implement the same behaviour using Elixir macros.

Turns out it was easier than I thought.

I don’t know if anyone is going to ever find this useful, but here it is, in all its glory.

defmodule Roc do
  defmacro a <- b do
    quote do
      retval = case unquote(b) do
        t = %Task{} -> Task.await(t)
        f when is_function(f) -> Task.async(f) |> Task.await()
        _ -> raise ArgumentError, message: "parameter b must be a Task or a function"  
      end
      var!(unquote(a)) = retval
    end
  end
end

and can be used this way

c <- Task.async(fn -> :return_value end)

d <- fn -> :function end

try do
  _e <- 2
rescue
  ex -> IO.inspect(ex)
end

IO.inspect({c, d})

# --- OUTPUTS
%ArgumentError{message: "parameter b must be a Task or a function"}
{:return_value, :function}

Most Liked

al2o3cr

al2o3cr

IMO this doesn’t quite capture the whole thing going on with ->; it’s missing the sequencing aspect of it.

From the “A Taste of Roc” talk:

This is the desugared form of:

username <- await (File.read "username.txt")
res <- await (Http.get "foo.com/\(username)")
File.write "response.txt" res

The presentation didn’t say it explicitly (at least not in the part I watched) but in addition to the above I assume Roc’s await is also doing some Result-monad gyrations to ensure that error values bubble out, ala Elixir’s with.

Note that starting a task and then immediately awaiting it in Elixir is kind of useless - the calling process is blocked while the Task executes and crashes if the task crashes. The “start then await” pattern is more of a Node idiom where await is the way to hand control back to the runtime.

massimo

massimo

Of course there’s no way that a 5 lines macro could replace the work being done on a compiler at the language level.

That wasn’t even the point.

As far as I understood what Roc does is “simply” avoid the callback hell, by ensuring that each call will wait until completion before the next one is started. The compiler can have a broader look on code at large, so has more information to optimize the calls, but what is doing is exactly “start and then await”, which is not bad per se. The calling process is blocked anyway when you await on something, in any language, or the order of the execution could not be guaranteed.
That doesn’t exclude that A is run concurrently with another computation somewhere else.

The indentation visible in the image you posted is used to scope the variables, every indentation in Roc creates a new scope (at least that was my understanding)

I don’t understand the error bubbling remarks: if the function being called return an error tuple, the error will be returned, if it raise an exception, the exception can be caught.

with gets a little help from the compiler

defmacro with(args), do: error!([args])

defmacrop error!(args) do
    quote do
      _ = unquote(args)

      message =
        "Elixir's special forms are expanded by the compiler and must not be invoked directly"

      :erlang.error(RuntimeError.exception(message))
    end
  end

Anyway, it was simply an exercise in metaprogramming to explore the possibility, I really thought it was not possible in Elixir, but I was wrong.

That’s why it’s posted in chat/discussions :slight_smile: :slight_smile:

Where Next?

Popular in Discussions Top

laiboonh
Hi all, I am trying to convince my team to use liveview over the current react. What are some of the points where one should consider us...
New
crispinb
On reading dhh’s latest The One Person Framework it strikes me that Phoenix with LiveView is already pretty much this. However, never hav...
New
nburkley
AWS re:Invent is on at the moment with some interesting announcements. One new feature in particular is the Lambda Runtime API for AWS La...
New
AstonJ
Are there any Elixir or Erlang libraries that help with this? I’ve been thinking how streaming services like twitch have exploded recentl...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
hazardfn
I suppose this question is effectively hackney vs. ibrowse but we are at a point in our project where we have to make a choice between th...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
ben-pr-p
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
scouten
I’m looking for a host for the server part of a small (personal) side project that I’m working on. It’s currently written in Node.js and ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New

We're in Beta

About us Mission Statement