weakty

First week of professional elixir

Hi there! o/

I get to write Elixir at my new job! This is my first time writing it in a professional setting. I have written Clojure for work and while Clojure and Elixir share quite a few similarities, I am learning lots of new things. Most of my learnings are around Elixir idioms and growing my knowledge of the concepts that the syntax of the language wraps around.

In a sense, this post is sort of a follow up to my post on side projects vs work for learning. The previous post is pretty… vague and hand-wavey, but this one will be a bit more concrete on some of the things I've learned. Let's jump into it.

defbehavior

I ran into defbehavior pretty quickly. In the moment it was a bit confusing - and I was mixing it up with protocols. And I'm not the only one!

Nonetheless, it didn't take too long to get used to it. A behavior is just a contract that specified that modules that use a specific behavior (defined in another module) must behave a certain way (ie. they must have certain functions).

I've never come across this in another language. Pretty cool!

Reduce tricks

I learned a really neat trick that uses reduce to loop over a map and change the accumulator to construct an ecto query.

def list_thing(query, criteria) do
  Enum.reduce(criteria, query, fn
    {:key_1, val_1}, query ->
      do_query_thing_1(query, klv)

    {:key_2, val_2}, query ->
      do_query_thing_2(query, val_2)
  )
  |> Repo.all
end

list_thing(MyStruct, %{key_1: :foo, key_2: bar})

The thing I hadn't really thought of doing was pattern matching inside the anonymous function. This way you can conditionally build up a value - the reduce it just going to return a query-ish value that Ecto later consumes with Repo.all.

Reduce is always a bit tricky for me to wrap my head around, so I'm looking forward to seeing it more often and getting more accustomed to it.

handle_info in LiveView

I'm already a fan of LiveView, but as I learn more about it I'm seeing that there's so much you can do with it! It really does eliminate a whole class of JS you'd have to write in an SPA. One thing I hadn't known about was the use of handle_info and the ability to send() messages to the LiveView itself. I haven't done this yet, so I might be getting this wrong. It happened to come up in a Reddit thread this week and so I asked about it and got a great overview. The idea that you can trigger a database query to run in the background and then automatically update the front end is just wild. I love it.

It really makes me want to understand what's happening behind the behind-the-scenes. I now know that LV uses Genserver, but how does Genserver even work? I want to dig down into the details of what Processes are on a fundamental level, and how they allow concurrent (or is it parallel? Always so easy to mix those two up) code to run on the BEAM scheduler. Maybe someday…

Reusable "front-end-components"

Since starting to use LiveView I've wondered how to best emulate front end reusable components a la React. It's quite simple to npm install antd and have a really powerful component library within your grasp.

In Elixir, the closest (but not in a bad way) is just use functions. One option is to define a module specifically for, say, styling your html elements. I use tailwind quite a bit, so that might look like this:

def btn() do
  "bg-green-500 hover:bg-green-600 text-white py-1 px-2 rounded self-start text-sm"
end

#... in your html

<button class={"btn()"}>

And then just use these functions to render the class strings whenever you need it, as above.

On top of that, you could make your btn class more dynamic by allowing a set of options to be passed in which would defer to different private functions:

def btn(opts \\ []) do
  size = Keyword.get(opts, :size, "medium")
  kind = Keyword.get(opts, :kind, "primary")
  Enum.join([btn_base(), btn_size(size), btn_kind(kind)], " ")
end

defp btn_base() do
  "inline-flex rounded self-start"
end

defp btn_size("small") do
  "text-sm p-2"
end

defp btn_size("medium") do
  "text-md p-4"
end

defp btn_kind("primary") do
  "bg-green-500 hover:bg-green-700 text-white"
end

defp btn_kind("secondary") do
  # etc...
end

I really like this idea. At first it seems a bit bleh to fill functions with a bunch of strings, but this way you are getting way less code in your templates (or smaller css classes if you are using @apply with tailwind) - and on top of that, the use of Enum.join prevents any need to worry about missing an empty space in conditionals that render different classes.


I'm looking forward to picking up many more tricks to come. I feel that, like Clojure, Elixir has a lot of nifty things you can do with it. I have yet to write a macro, for example (or a protocol). I'll be sure to check in with future learnings.

Thanks for reading!

o/

WT