guards in Elixir 1.6

Elixir 1.6 has been released with a lot of nice additions to the language, but without a doubt, what I’m most pumped about is real guard support in the language.

Before 1.6, you could constraint a function like so:

def change_settings(%User{admin: is_admin}, settings) when is_admin == true do
    # Update the settings is only available for admins
end

This worked just fine, but now with real guard support, we can extract those checks to make reuse and composition easier!

defmodule User do
  defstruct [:karma, :friend_count]
end

defmodule Main do
  defmacro can_update_settings(karma, friend_count) do
    quote do
      unquote(karma)> 100 and unquote(friend_count) > 3
    end
  end

  def change_settings(user = %User{karma: karma, friend_count: count}, settings) when can_update_settings(karma, count) do
    IO.puts "Updating settings..."

    {:ok, user}
  end

  def add_member(user = %User{karma: karma, friend_count: count}, new_user) when can_update_settings(karma, count) do
    IO.puts "Adding member"

    {:ok, user}
  end

  def main do
    %User{karma: 101, friend_count: 4}
    |> change_settings(%{})
  end
end

Main.main

// => "Updating settings..."

You can skip the manual macro creation alltogether if you use defguard and defguardp, though. These are new functions in the Kernel module that just take your constraint and generate a macro suitable for use as a guard from it.

defguard can_update_settings(verified) when verified == true

I specially love the fact that now you can also use multiple guards with functions:

defmodule User do
  defstruct [:karma, :friend_count]
end

defmodule CommunityFactory do
  defguard has_enough_karma(karma) when karma > 100
  defguard has_invited_enough_friends(friend_count) when friend_count > 3

  def create_community(%User{karma: karma, friend_count: count}, com_name)
    when has_enough_karma(karma)
    when has_invited_enough_friends(count) do

    IO.puts "Creating community..."

    {:ok, %{name: com_name}}
  end

  def create_community(_user, com_name) do
    IO.puts "Cannot create #{com_name}. User still does not complete onboarding."

    {:error, nil}
  end

end

defmodule Main do
  def main do
    %User{karma: 101, friend_count: 5}
    |> CommunityFactory.create_community("My Community")
  end
end

Main.main

// => Creating community...

I use guards like crazy in Swift. I think they’re a great tool to structure your code. However, in Swift, guards can be used (and maybe abused) more as a fancy if/else dance, rather than a mean to express condition conformance.

For instance, in Swift, the only requirement for a guard is that it breaks the flow of the function:

guard user.isAdmin else { 
    print("User is not admin!")  # Compiling error, does not break function flow
}

// Update settings here

But literally nothing stops you from transferring the control over to another function!

guard user.isAdmin else {
    showErrorMessage(.notAdmin) // Give up control of the execution flow
    return
}

// Update settings here

Although this may seem harmless, it can cost some precious down the road when you’re trying to debug something that just follows a trail of guarded statements that keep on going forever.

With Elixir’s flavor of guards, this is explicitly not possible:

Not all expressions are allowed in guard clauses, but only a handful of them. This is a deliberate choice: only a predefined set of side-effect-free functions are allowed. This way, Elixir (and Erlang) can make sure that nothing bad happens while executing guards and no mutations happen anywhere.

Altough I love Swift, I do think that its guard does not entirely guard you from shooting yourself in the foot.


Small but thoughtful updates like this to the language really make me hopeful for the future of Elixir.

You can read everything about the new Elixir guards on the official documentation page and read the release announcement here.

Experimenting with inherited tables in Ecto

I’ve been working on a personal project with some mates for the past few months, and I’ve been mostly responsible for building our backend with Phoenix.

It’s come to a time where we need to implement some sort of "comments & likes" functionality for the app, so I set out to start thinknig how to go about it.

Note: this is just me experimenting, not trying to say that this is the right way to do it. I’m still learning about these things, so if you think I’m missing something, by all means, let me know at oscar@swanros.com

Note 2: the example code for this post is on GitHub.

When I started digging around, most of the answers I found about how to implement such functionality pointed to using table inheritance so that I could have a ActionableEntities table, and EntityComments and EntityLikes tables. Then, Posts, Photos, Events whould inherit from ActionableEntities gaining the ability of being commented or ‘liked’.

"Simple enough," I said to myself. Then I started digging. This is what I found.

Implementing table inheritance with Ecto

I couldn’t find concrete examples of how to do this, but did read the official Ecto documentation, though, and found that you can pass an :options parameter to specify extra attributes that you want your table to have, such as WITH, INHERITS or ON COMMIT. So, the migration looks like this:

def change do
    create table(:actionable_entities) do
        timestamps()
    end
    
    create table(:entity_comments) do
        add :content, :string
        add :entity_id, references(:actionable_entities)
        
        timestamps()
    end
    
    create table(:posts, options: "INHERITS (actionable_entities)") do
        add :content, :string
    end
    
    # photos and all other tables follow the same structure as the posts one.
end

Now the modules that’re going to be using each of these tables are defined as follows:

defmodule Inh.ActionableEntity do
  use Ecto.Schema

  schema "interactive_entities" do
    has_many :comments, Inh.EntityComments.Comment

    timestamps()
  end
end

defmodule Inh.EntityComments.Comment do
  use Ecto.Schema

  schema "entity_comments" do
    field :content, :string
    belongs_to :entity, Inh.ActionableEntity

    timestamps()
  end
end

defmodule Inh.Posts.Post do
  use Ecto.Schema

  schema "posts" do
    field :content, :string

    timestamps()
  end
end

"This ought to work right here," I thought to myself. And it does for the most part: I can create a post, and get a list of posts. I can even query for a Post‘s comments eventhough there are none on the database.

However, the following error on the database arises when trying to create a comment for a given post:

ERROR: insert or update on table "entity_comments" violates foreign key constraint "entity_comments_actionable_entity_id_fkey" DETAIL: Key (entity_id)=(1) is not present in table "actionable_entities".

The code used for trying to insert a new comment looks something along the lines of

def create_for_post(id, c_params) when is_integer(id) do
    Repo.get(ActionableEntity, id)
    |> Ecto.build_assoc(:comments, c_params)
    |> Repo.insert()
end

First get the post I want to comment, build the corresponding association and then insert it into the database. Here’s the problem, though:

indexes (including unique constraints) and foreign key constraints only apply to single tables, not to their inheritance children.

So, if I add a record to posts or to photos, the information from the inherited table bubbles up to teh parent table. But then, technically, the information is a post, not an actionable_entity. 🤔

Postgres is right to be telling me that there’s no ActionableEntity with the given id.

I could get more of a sense that something is really wrong here by creating a new photos table that too inherits from actionable_entity and adding some records to it:

At this point, there’s no data consistency as the database can’t distinguish between Photos and Posts.

One way to solve this is to create a trigger on the database to check every time we try to inset a new EntityComment for a Post, that the Post indeed exists on the database. To do this, the foreign key constraint needs to be removed from the database. So first, update the entity_comments migration:

create table(:entity_comments) do
  add :content, :string
  add :entity_id, :integer

  timestamps()
end

entity_id is now just a simple :integer, there’s not an explicit reference to the actionable_entities table.

Then, create the trigger:

execute """
  CREATE OR REPLACE FUNCTION internal_post_check() RETURNS TRIGGER AS $$
  BEGIN
    IF NOT EXISTS(SELECT 1 FROM posts WHERE id = new.entity_id) THEN
      RAISE EXCEPTION 'Post does not exist:  %', new.entity_id;
    END IF;
    RETURN new;
  END;
  $$ language plpgsql;
"""

execute """
  CREATE TRIGGER CheckEntityExists BEFORE INSERT OR UPDATE ON entity_comments
  FOR EACH ROW EXECUTE PROCEDURE internal_post_check();
"""

This trigger will run every time a new record wants to be inserted on the entity_comments table and will manually check if a post with the value on entity_id as id exists.

It may seem that the problem is now solved, but then again, what would happen when I have a Post and want to retrieve its comments? If I have Posts and Photos and potentially N number of "interactive entities" on my database, how would I be able to query just for those?

I solved this by adding a new type:String column on the actionable_entity table. Posts would have the value post in that column, photos would have the value photo, and so on for every type of actionable entity I eventually add to the database.

This way, now I can query for the comments of a specific photo with a given id:

def comments_for_post(id) when is_integer(id) do
    q = from entity in ActionableEntity,
        where: entity.id == ^id and entity.type == ^"post",
        left_join: comments in assoc(entity, :comments),
        preload: [comments: comments]
        
    post = Repo.one(q)
    post.comments
end

Final notes

Although this works it does require to bypass the database’s integrity checks to handle those on my own. This is very error prone and would require me to constantly run tests to verify that I’m not missing addig a new trigger for photos, events, or any other "actionable entity" that I want to add to my system.

Also, if I decide to add another kind of action to these entities, such as "likes" or "claps," I’d have add another set of checks for those too.

This is very soon becoming a maintainability nightmare.

I asked friend whose really experienced with backend development and he just sent me this link to a post called Three Reasons Why You Shouldn’t Use Single Table Inheritance. Read it.

In the end, this was just me trying to implement a solution that I thought would make for a good one, but it seems that the compromises that need to be made here are not worth it.

What I went with was the simplest approach: add to the entity_comments table the columns of the entities that I want to enable comments for:

id | content | post_id | photo_id | event_id | inserted_at | updated_at
-----------------------------------------------------------------------
1    Hey!      1                               -----         -----
2    nice!               1                     -----         -----
3    Good!     4                               -----         -----

As stated in schema.ex#L806:

Unless you have dozens of columns, this is simpler for the developer,
more DB friendly and more efficient in all aspects.

So there’s that! Thanks for reading.

Résumé

Oscar Eduardo Cisneros Larios, Sr. iOS Software Engineer

Download PDF version.

+52 312 171 0694 | oscar@swanros.com | @swanros

Oscar is a self-taught iOS Developer with more than 6 years of experience and a passion for education and turning hard technical problems into readable text that anyone can comprehend. Living in Colima, Mexico, Oscar has been working remotely for the past few years for various companies in the U.S., specifically in the San Francisco Bay Area, such as OneLogin, michelada.io, and CloudApp.

As an iOS Developer, Oscar focuses on the app’s core architecture, using his expertise to look into the future, and give the application in question the ability to be resilient to changes that may come down the road.

Skills

  • Swift and Objective-C programming for iOS and macOS.
  • Elixir programming, mainly used for web APIs and implementing tooling for iOS and macOS app deployment.
  • Framework-oriented architecture design.
  • Service design and implementation using Elixir/Phoneix.
  • UI/UX design using Sketch.

Passions

  • Teaching & Education. Oscar comes from a lineage of teachers that have marked the life of generations in his hometown. He learned from a young age the importance of being up to date on what it is that interest him, and the value of helping others building up knowledge and confidence. One of his goals is to make programming more accessible to Spanish-speaking audiences — to that end, he has done free workshops, written blog posts, and given talks in Spanish about iOS programming and industry best practices. aprendeios.com is his current endeavor in this area.
  • Music. Before settling for Systems Engineering as a major for college, Oscar was seriously considering pursuing a career on music. He learned to play piano at age 6, played guitar through elementary and middle school and settled for drums, which remain his favorite instrument. You can often see Oscar playing air drums or air guitar in while the code is compiling, or whenever a breakdown is about to happen. Oscar apologizes in advance if you’re with him at a public place and a band is playing — he’ll be watching them.
  • Technology. From very early age, he demonstrated a fascination for technologies and its different applications — from small, quirky utility clocks, to phones and software. One of the projects Oscar is the proudest, is an online community he built while on high school that was comprised of a news site and YouTube channel built around Apple news. SwanrosTech, as it was named, took off slowly but steadily, getting almost 10k daily visits to the webpage and more than a million views on YouTube, right before Oscar decided to shut the project down to focus on college.

Notable Experience

Sr. iOS Software Engineer, CloudApp. Oct 2016 — Current.
At CloudApp, Oscar worked on rebuilding the foundation for the CloudApp macOS application, with the goal that the iOS and macOS clients for the service shared a unified code base. One of his main focus was helping the Apple Platforms team to establish standards and best practices across working areas, such as code review, release process, and architecture. Oscar also worked closely with the backend team to create new micro-services with the goal of automating the team’s Continuous Integration and Continuous Delivery goals.

Sr. iOS Software Engineer, michelada.io Apr 2016 — Oct 2016
Oscar joined the michelada.io team to help with some short-term client projects that needed iOS components. Through constant communication and feedback, the michelada.io team was able to cater to company’s clients necessities. Oscar worked closely with the michelada.io team to help clients transform their ideas into working products on iOS devices, guiding them through the idea, design, and implementation processes.

Mobile Software Engineer, OneLogin Mar 2014 — Jul 2015
While working at OneLogin, Oscar rebuilt the iOS application that the company’s clients use to access their secure infrastructure and applications. He also helped design and implement OneLogin’s NAPPS architecture, which enables secure communication and authentication between native applications on both iOS and Android.

Other
Oscar started working professionally as an iOS/web developer in 2013 at a local marketing firm doing apps for local clients. Today, when time permits, Oscar also works with different clients and companies as an aide to help them revamp their mobile offering. These endeavors are often carried on on a per-contract basis. Most of the contract work that Oscar has carried out has to do with taking UI design to implementation and helping review architectural decisions.

Education

Oscar started college at Instituto Tecnológico de Colima with the intention of majoring in Systems Engineering. However, he soon realized that the academic and technical level of the institute’s offering was not what he was expecting, and was not going to help him accomplish his objectives.

Being a natural autodidact learner, Oscar has been focused on learning topics and mastering tools that actually matter to him ever since dropping out of college.

What Oscar looks for in a company

  • A platform to do work that impact people’s lives in a positive way.
  • Working on interesting problems related to his passions.
  • Working with people that are aligned with his vision.
  • For it to enable self growth, both on a personal and professional level.

action_fallback and contexts in Phoenix 1.3 made my controllers tiny!

Yesterday I posted about how Phoenix 1.3 was pure love for API development. In that post, I mentioned that one of my favorites features in this new release is the action_fallback plug.

Today, I want to talk a little bit more about a killer combo I’ve uncovered: action_fallback + contexts.

I’m still amazed at how Phoenix 1.3 enables my controllers to be so tiny.



# Background
In my application, I have a `User` module that’s managed through an `Accounts` context:

def update(conn, %{"id" => id, "user" => user_params}) do
  user = Accounts.get_user(id)

  with {:ok, %User{} = user} <- Accounts.update_user(user, user_params), 
  do: render(conn, "show.json", user: user)
end

The default MyApp.Web.FallbackController that’s generated when you first create your app includes, amongst others, a call/2 definition that accepts an %Ecto.Changeset{} instance:

defmodule MyApp.Web.FallbackController do
  use MyApp.Web, :controller

  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    conn
    |> put_status(:unprocessable_entity)
    |> render(MyApp.Web.ChangesetView, "error.json", changeset: changeset)
  end

  # 
end

This function will be called if, for instance, the update/2 method on UserController failed to pattern match the with statement. Repo.update/1 returns {:error, changeset} in case it fails. Since I didn’t define an else path, {:error, changeset} would be then forwarded to MyApp.Web.FallbackController.



# Custom Errors
I like to define custom errors for the domains for my app. The default behavior on `MyApp.Web.ChangesetView` will output something like this:

{
  errors: [
    "username has already been taken"
  ]
}

I modified MyApp.Web.ChangesetView to expose the error the way I wanted:

defmodule MyApp.Web.ChangesetView do
  use MyApp.Web, :view
  alias MyApp.ResponseWrapper

  def translate_errors(changeset) do
    case hd(changeset.errors) do
      {label, {"has already been taken", _}} ->
        "#{label}_taken"
      {:password, {"should be at most %{count} character(s)", _}} ->
        "password_too_long"
      {:password, {"should be at least %{count} character(s)", _}} ->
        "password_too_short"
      _ ->
        :something_went_wrong
    end
  end

  def render("error.json", %{changeset: changeset}) do
    ResponseWrapper.error translate_errors(changeset)
  end
end

In the translate_errors/1 function, I take the first error off the changeset, and then pattern match its content. I can add more patterns to match later depending on my needs. Now, MyApp.Web.ChangesetView outputs errors like this:

{
  status: "error",
  error: "username_taken"
}

{
  status: "error",
  error: "password_too_short"
}



# Benefits
What’s great about that, is that now I can basically reuse that same code for any other module that needs to be inserted in my database.

This lets me write code like this:

defmodule MyApp.Web.DeviceController do
  use MyApp.Web, :controller

  alias MyApp.{Authentication, Notifications}

  plug :scrub_params, "device" when action in [:create]
  plug Authentication
  action_fallback MyApp.Web.FallbackController

  def create(conn, %{"device" => device_params}) do
    with {:ok, user} <- Authentication.get_current_user(conn),
         {:ok, _dev} <- Notifications.add_device(device_params, user),
         do: :success
  end
end

My create/2 function in DeviceController is just 3 lines long. In those 3 lines, I:

  1. Get the current user.
  2. Insert a new device (with changeset validations) to the database.
  3. Passes :success to my the action_fallback plug.

The “happy path” response from my fallback looks like this:

{
  status: "success",
  data: {
    status: "success"
  }
}

If any of the guards in the with statement fails, guess what: my fallback controller’s got my back 😬.

For instance, say the Notifications.add_device/2 function fails to insert the new device into the database because it didn’t meet one of the changeset validations, such as the unique_constraint for device_token.

Notifications.add_device would then return {:error, changeset}, which would be forwarded to my fallback controller, and the JSON error would look like this:

{
  status: "error",
  error: "device_token_taken"
}

And I got that for free. 👆

Yay contexts! Yay action_fallback! 🎉

Phoenix 1.3 is pure love for API development

Phoenix 1.3-rc.0 was just released, and while it’s still not a final release, it already feels solid as h*ck.

I’ve been doing a personal project on the side for a while now, and it has a web API component, which I wrote in Phoenix 1.2. That was my first time using Phoenix on a serious project that I intend to ship.

With the release of Phoenix 1.3, and after I watched Chris McCord’s talk about the changes it brings to the table, I decided to rewrite my API from scratch with this new version.

And oh boy, is it great.

Here are my first-impressions.

New Folder Structure

It has been said a lot of times that Phoenix is "Rails for Elixir." With 1.3, the core team sure wants that notion to be as dead as the web/ folder and the model concept.

I’m not going to explain everything that Chris talked about on his keynote at Lonestar Elixir (you should seriously go watch it), but I think it’s worth saying that the change in the folder structure of a Phoenix project has bigger implications that just files moving from one place to another.

Now, there are concrete boundaries between your actual Elixir application, and the interface that Phoenix provides for it to communicate with the web.

Responsibility separation by design. I love this.

Thinking Ahead

Long gone are the mix phoenix.* commands. Say hi to mix phx.*. Less things to type. Love it.

Also, the generators will make you think in advance of what you want to do before you actually do it.

For instance, in Phoenix 1.2 you could do something like this:

$ mix phoenix.gen.json User users email:string

which then would generate the right migrations, and the model:

defmodule MyApp.User do
    use MyApp.Web, :model

    schema "users" do
        field :email, :string
    end
end

along with a (web) controller that would handle the CRUD tasks, with the logic for validation within each action. Over time, using this structure, is easy to end up with controller actions over hundred lines long.

Now, with Phoenix 1.3, you need to specify a context for each resource you create (don’t call them "models" anymore 🙄):

$ mix phx.gen.json Accounts User users email:string

In the command above, Accounts is the context module that’s going to be also generated, and is through this context that we’ll interact with our User module:

Accounts.get_user(2) #=> %User{email: oscar@swanros.com}
Accounts.create_user(params) #=> {:ok, new_user}

This is powerful, because now you can define clear boundaries for your application domains.

Say you have the concept of "contacts" in your application. Each user (User) has contacts (also User instances). This can get messy (as I’ve written before). But using this new notion of "contexts", we can have a ContactRelationship context and handle everything in a clean way:

user
|> ContactRelationships.relationship_with(another_user)
|> ContactRelationships.accept

user
|> ContactRelationships.get_contacts

user
|> ContactRelationships.request_contact(another_user)

This scenario would make our folder structure look something like this:

|- lib/
|---- my_app/
|-------- accounts/
|-------- contact_relationships/
|-------- web/

Defining concrete boundaries between responsibilities within your app makes you come up with better code over time.

action_fallback

So far, the additions to the framework are really nice and welcomed. However, I think my favorite one is the new action_fallback plug.

On Phoenix 1.2 and earlier, every controller needed to return a valid conn for every request. Otherwise, an exception would rise.

This is still true, but with the new action_fallback plug we can delegate that task to another controller whose sole purpose is to handle the cases when other controllers couldn’t provide a "successful" response to the request.

Let me explain… before, you wrote code similar to this in every controller action:

def create(conn, %{"user" => user_params}) do
  user = Repo.get_by(User, phone_number: user_params["phone_number"])

  cond do
    user && checkpw(user_params["password"], user.password_hash) ->
      case create_session(user) do
        {:ok, session} ->
          conn
            |> put_status(:created)
            |> render("show.json", %{session: session, user: user})

        _ ->
          conn
            |> put_status(:unauthorized)
            |> render("error.json")
      end

    true ->
      dummy_checkpw()
      conn
        |> put_status(:unauthorized)
        |> render("error.json")
  end
end

Is not that bad, truly. But this is still better with Phoenix 1.3:

action_fallback MyApp.Web.FallbackController

def create(conn, %{"user" => user_params}) do
  user = Accounts.get_user_by_phone(user_params["phone_number"])

  cond do
    user && checkpw(user_params["password"], user.hashed_password) ->
      with {:ok, %Session{} = session} <- Sessions.create_session(user) do
        conn
        |> put_status(:created)
        |> render("auth_success.json", user: user, session: session)
      end

    true ->
      {:error, :wrong_credentials}
  end
end

With Phoenix 1.3 our controller actions can be simplified to just care about the "happy path", and leave the rest to the fallback controller that we define using the action_fallback plug.

In this case, when the cond evaluates to true we return from the action {:error, :wrong_credentials}. Since this is not a valid connection, our fallback controller comes forward:

defmodule MyApp.Web.FallbackController do
  use MyApp.Web, :controller

  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    conn
    |> put_status(:unprocessable_entity)
    |> render(MyApp.Web.ChangesetView, "error.json", changeset: changeset)
  end

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(:unauthorized)
    |> render(MyApp.Web.ErrorView, "auth_required.json")
  end

  def call(conn, {:error, :wrong_credentials}) do
    conn
    |> put_status(:unprocessable_entity)
    |> render(MyApp.Web.ErrorView, "wrong_credentials.json")
  end
end

By pattern matching, the fallback controller knows exactly what to send back as a response to the request.

This also means that we can have a centralized list of error codes that our API can expose to our clients, on our ErrorView module.

No more error definitions scattered around our codebase. ❤️

However, why stop with errors? If your API has "default" responses for actions, you can rely on the fallback controller too to display them:

def delete(conn, _params) do
  with {:ok, session} <- Authentication.get_session(conn),
       {:ok, deleted} <- Sessions.delete_session(session) do
    :success
  else _ ->
    {:error, :unauthorized}
  end
end

:success will be passed to the appropriate action on your fallback controller, and there goes your default response. 🎉

Final thoughts

Phoenix 1.3 is a great update.

Please keep in mind that the defaults 1.3 offers could be done with 1.2, too. But the fact that this is now the default, really speaks of how much attention to detail the Phoenix team is putting in.

They don’t want us to be using "The Phoenix Way™", but rather provide sensible defaults that enable us to create our own solutions in the best possible way.

Some points:

  • The new fallback_controller plug is awesome.
  • The fact that you need to think in terms of boundaries for your app, I think will eventually lead to better, more maintainable codebases.
  • Smarter code generation is 👌.
  • For APIs, the centralized list of available errors is really welcomed.

I love how productive I am with Phoenix when building APIs, and I’m really looking forward to how this already-great framework will evolve past 1.X.


edit: I also wrote about how action_fallback and contexts in Phoenix 1.3 made my controllers tiny.

Bringing piping syntax from Elixir to Swift

I’ve been writing Elixir for my main backend needs for the past few months and I’ve really liked it — specially its syntax. In fact, I’ve gotten so into its syntax that I’ve often found myself trying to write Swift as if it were Elixir.

Oops!

So I decided to experiment a little bit with bringing my favourite syntax from Elixir over to Swift natively.

Disclaimer: This is just an experiment. To get this to work on an actual project, a lot more work needs be done.


Elixir is a functional language that’s gained a lot of traction in the past few months thanks to its simplicity and core features, such as the supervision tree.

Every type in Elixir is a value, which means that it gets copied when passed to a function — the function gets a new copy of the value, which it can then modify without affecting the original instance.

> aMap = %{a: 1}
  %{a: 1}

> bMap = Map.update(aMap, :a, 0, &(&1 * 3))
  %{a: 3}

> aMap 
  %{a: 1}

> bMap
  %{a: 3}

Also, Elixir has this great “pipe” operator that lets you write cleaner code and streamline data manipulation in a really easy way:

> cMap = aMap |> Map.update(:a, 0, &(&1 / 3))

The “pipe” (|> ) operator takes the value on its left and passes it to the function on the right as its first parameter. This is included as part of the language and works with any function.

Let’s bring that to Swift!

In this example, I’m going to use two simple structs (value types in Swift):

struct Company {
    let name: String
}

struct Person {
    let name: String
    let work: Company
}

let apple = Company(name: "")
let joe = Person(name: "Joe", work: apple)

A characteristic of functional programming languages is that they strive for immutability. Vaues should not mutate during program execution, but copying values and modifying them is good.

Tipically, if we wanted to update joe‘s name, we’d do something like

joe.name = "Joe Wood"

If we did this, the Swift compiler would raise two errors:

  1. the name property on Person is declared as a constant (let), which means it can’t mutate during execution — a change to var is suggested.
  2. the joe instance was declared as a constant (let), which means it can’t mutate during execution — a change to var is suggested.

What we don’t want, though, is enable the ground to shift under our feet, so let’s keep everything as a constant.

Another approach would be to create a function that did so:

extension Person {
  mutating func update(name: String) {
    self.name = newName
  }
}

But again, that mutating keyword there is a code-smell in the functional world. The way to do this the functional way is:

extension Person {
  func update(name: String) -> Person {
    return Person(name: newName, work: self.work)
  }
}

We created a function that changes only one property on the instance — it receives the new value for the property, and returns a whole new instance of the type with that value replacing the original one.

joe.update(name: "Joe Wood").name // Joe Wood
joe.name                          // Joe

Now, the problem with this is that the function that we created is operating on the type-instance.

Note: In Elixir, a map literal can be written as %{a: 1}, but to transform and operate on that instance, we have to use the Map module. That’s kind of what we want to achieve here.

We want that the Person struct (or module, if you’re thinking in Elixir) to have a series of methods that receive Person instances to operate with.

In order to achieve that, we need to do a bit of groundwork.

Lenses

There is a concept in functional programming called “data lenses”. Explaining in detail what a data lens is, is way out of scope of this post, but you can read more about it here and here, and generally, just searching “lenses functional programming” in any search engine.

The idea is to have a data type (a Lens) that is generic and lets us get and set (a.k.a. zoom to) information on the type that the Lens is generic to.

Example:

struct Lens<Whole, Part> {
  let get: (Whole) -> Part
  let set: (Whole, Part) -> Whole
}

let personNameLens = Lens<Person, String> (
  get: { $0.name },
  set: { Person(name: $1, work: $0.work) }
)

personNameLens.get(joe)                  // Joe
personNameLens.set(joe, "Joe Wood").name // Joe Wood

That’s a Lens. However, we’ll only need a subset here, so let’s define a new data type that gives us only the set method:

struct SetLens<Whole, Part> {
  let set: (Whole, Part) -> Whole
}

To update a Person instance, we can do this now:

let setPersonNameLens = SetLens<Person, String> (
  set: { Person(name: $1, work: $0.work) }
}

setPersonNameLens.set(joe, "Joe Wood")

Now lets do a bit of work to move the interface up to type-level by making our SetLens a static property of Person, for brevity:

extension Person {
  static let SetName = SetLens<Person, String> (
    set: { Person(name: $1, work: $0.work) }
  ) 
}

Person.SetName.set(joe, "Joe Wood")

We still have the problem that we’d have to interact with the SetLens directly. To mimic the Elixir behaviour, we need to wrap the SetName property on a static function that returns a function that when called, calls the set method on the lens.

extension Person {
  static func Update(name: String) -> Person -> Person {
    return { p in SetName.set(p, newName) }
  }
}

We define a static function that takes a new name as a parameter, and returns a function that takes a Person and returns a Person. Calling this function just passes the values to the SetLens.

Now, let’s introduce the pipe operator:

precedencegroup Applicable {
    associativity: left
}
infix operator |>
func |> <A, B>(a: A, rhs: (A) -> B) -> B {
    return rhs(a)
}

This new custom operator takes a value to the left and a function on the right, and just calls the function on the right passing the value on the left. How convenient!

We can now do this:

joe |> Person.UpdateName("Joe Wood")

Ta’da!

Doing the appropriate amount of work, we could be writing this really soon:

joe
  |> Person.Update(name: "Joe Wood")
  |> Person.Update(work: Company(name: "Apple"))

And voilá, we’ve migrated one of Elixir’s core features to Swift, while maintaining type-safety!

Final comments

Admittedly, it took a long of work just to migrate this one feature over to Swift. This does not mean, though, that it can’t be done for big code-bases and “real apps.” One of the best features of Swift is that the compiler is real smart and can tell us on compile-time if we messed something up. ❤️

This was just a small exercise, but I do appreciate the similarities that Swift and Elixir have, and the fact that both languages are flexbile enough that we can do these sort of things. Pretty cool.


Hey! I’m writing a book on best practices for iOS development with Swift, and it’s called (drum roll) Best Practices for Developing iOS Apps with Swift.

Subscribe below to get a 50% off coupon code when the book is released — also get the first chapter for free when it is ready!

Filtering has_many relationships in Ecto

This is sot-of a follow up to my last post, about self-referencing many_to_many relationships using Ecto.

I find myself in a scenario where I have a User:

# User model
defmodule MyApp.User do
  use MyApp.Web, :model

  alias MyApp.Contact

  schema "users" do
    has_many :_internal_contacts, MyApp.Contact
    has_many :contacts, through: [:_internal_contacts, :contact]

    timestamps
  end
end

and an association model Contact through which the User has many contacts (User instances):

defmodule MyApp.Contact do
  use MyApp.Web, :model

  alias MyApp.User

  schema "contacts" do
    field :status, :integer

    belongs_to :user, User, foreign_key: :user_id
    belongs_to :contact, User, foreign_key: :contact_id

    timestamps
  end

  def status_code(status) do
    case status do
      :accepted ->
        1
      :pending ->
        0
      :rejected ->
        -1
      _ ->
        0
    end
  end

I created an endpoint on my app that’s supposed to get me only the user’s contacts that have the :accepted status (1). How can this be accomplished?

The Repo module has a set of handy functions that let you preload associations on your models.

user = User |> Repo.get(1) |> Repo.preload(:contacts)

The above will preload all contacts on the user. Pay attention to the fact that it is of type has_many and it goes through the :_internal_contacts property. This is the important cue.

Turns out that the preloads parameter on Repo.preload/3 can be accompanied by a query.

import Ecto.Query

#1
query = from c in Contact, where: c.status == 1 and c.user_id == 1

u = User
    |> Repo.get(1) #2
    |> Repo.preload(_internal_contacts: query) #3
    |> Repo.preload(:contacts) #4

The code above will:

  1. Create a query that will ask for Contacts with status 1 and with user_id 1.
  2. Get a User instance.
  3. Preload the _internal_contacts on that instace using our query (these are Contact instances, not User instances)
  4. Preload the :contacts on our user. And since :contacts goes through: _internal_contacts, :contacts has only valid contacts now (User instances).

Notes:

Honestly, I don’t know if this is the right approach here (let me know!). I did find this thread on GitHub where the general issue that we’re facing here is described. It seems that there’s an interest to have the ability to filter the relationships on the declaration itself:

It would look something like this:

has_many :comments, MyApp.Comment, foreign_key: :commentable_id,  fn (query) -> 
  from c in query, where: c.commentable_type == "articles"
end

However, right now, the saner approach seems to be the use of composable queries.

Have anything to add to this article or did I miss something? Please let me know on Twitter.