Having problems learning elixir

This hasn’t yet happened to me - usually I learn new things fast and enjoy it.
But with Elixir, I’m having serious problems (or most likely with functional programming in general),
even though I really want to learn it.

I’m following the book Functional web development with Elixir, OTP and Phoenix.
And I also bought a book only teaching Elixir.

I understand the examples (though it usually takes a while).
But I have no clue, how I can get to a point where I can come up with solutions like that myself.
What’s even more confusing is everyone writing about how easy and clear Elixir as a language is,
how they were up and running after a week, while I’m struggling so much.
Could it be because I have been programming in other paradigms for a long time (12 years now)?

How did you learn functional programming? Or not learn it…how did you get to a point, where you could
come up with your own solution in a functional language?

4 Likes

What exactly is your problem with functional/elixir?

Immutability? Recursion? Enum? Not having objects?

My learning story of FP was mainly driven by a introductionary Haskell Course at the university during the first or second semestre.

4 Likes

Yes, that can be the cause. The first tip to learning FP: Forget everything you know about programming. You have to understand what FP means when talking about function and immutable.

I have started learning Elixir with " Programming Elixir 1.X" by Dave Thomas, and for me, that is a great first book.

3 Likes

It takes some time for me to learn functional programming. The mental model is very different from OOP. One book which I really like is Programming Elixir 1.6 by Dave Thomas. Something that strikes me about functional programming is about data transformation. The function takes in some data, do something with it, and return the result while still keeping the original data.

In this process, the function never mutate the original data. It always returns a copy of it. Once I got this, quite a few things about Elixir begins to make sense.

I read somewhere functional programming is actually more similar to functions in our high school mathematics than OOP. It is supposed to be simpler to reason about as it is what we are used to.

2 Likes

I’m learning elixir myself, so don’t consider this as an expert’s response. I mainly work with JS/TS/NodeJS. That’s just for context.

My learning started when one of my colleagues made a workshop at work, introducing elixir. Initially, it was quite overwhelming, too much “noise”. Too many new concepts (pattern matching, processes, tuples), syntax (completely different from JS world for example, even small things like snake case vs camel case used in JS) and I’m not mentioning deep concepts of functional programming(but it is quite common to try to write functional code in JS, so maybe some concepts were easier to grasp). After workshop series (three 2 hour session in the course of 2 month), i basically dropped it.

After 8 or 9 months, I wanted to work on a side project, and wanted to learn something new. I picked up elixir again, and surprisingly (or maybe not) it was easier. There are a lot of concepts that I still don’t really grasp (hello OTP), I have no idea what’s the proper way to structure application etc. BUT, I have a running application, that I want to share here soon :slight_smile:

So, in retrospect, I think what was different on my second attempt is

  1. I had an introduction to the language basics and syntax. keywords, pattern matching etc were not new to me, maybe i still didn’t know them well, but it wasn’t something overwhelming
  2. My goal was different, I didn’t want to just learn elixir, I wanted to create my project, learning elixir just came with it. In my second attempt, I actually started with phoenix(after quickly refreshing language syntax), I bought programming phoenix (PragProg) and just followed it until the point where I had a running system with authentication and few pages. I didn’t go further, I was itching to start my project. Similarly I went through Dave’s course, Bruce Tate’s LiveView course (without actually finishing them, not because they are not good, but because information i got was enough for me to continue)
  3. I started working on my project. Right now it is some sort of frankenstein, where I am sure, some concepts are not properly used, best practice is not followed, and even code structure is not right, but I’m fine with that. It allowed me to consume concepts, reach for tools as I need them. For example, I was so afraid of Processes, what is a Task, what is an Agent, how do I implement GenServer. All intimidating words, but I ignored them, until I had my app up and running, then I thought, maybe I can improve it with those concepts and after one evening, I had a grasp, how it could improve (and how powerful it is) my project. Also, I am itching to rewrite it now, in a more unfrankenstein-y way :slight_smile:

Rereading your question, this might be not the answer you are looking for :sweat_smile:, but it’s a perspective. This is the approach that works for me, where I need to stay motivated, after workday and family.

What worked for me is focusing on the project, and finding out how to use elixir/phoenix to accomplish it. It’s like learning new natural language while being in the country where they natively speak it.

9 Likes

I understand the code examples when I stare long enough at them, and read up on the different language constructs. But I feel so far away from being able to come up with those solution myself.

One problem for example is reduce. I know what it does in theory, and I understand what it does in examples.
But for me, if I try to solve problems myself, it never is clear when I have to use it.
Why reduce? Why not just iterate over a collection?

There is no problem with recursing over a list on your own.

Enum.reduce/3 is just an abstraction. A common pattern that has been moved into its own function.

In elixir it has also the side effect of working with any “iterable” type, a manually implemented recursion usually restricts itself to lists only.

iex(1)> [1,2,3,4,5,6,7,8,9,10,8,6,4,2] |> MapSet.new() |> Enum.reduce(0, &+/2) 
55

Though perhaps tell us which languages you come from? We can then provide examples that are more familiar to you?

1 Like

reduce iterates over a collection, but with an accumulator, to reduce this collection to a single item.

When You stare into your code, your code stares back at You :slight_smile:

5 Likes

Lets take an example:
In pseudo code:

list = [1, 2, 3]
sum = 0

for x in list {
  sum = sum + x
}

sum is mutable your functionality is in the loop.
Elixir:

defmodule Foo do
  defp add(a, b), do: a + b

  def sum(list) do
    Enum.reduce(list, 0, &add/2)
  end
end

Foo.sum([1,2,3])

Now, you have your functionality in add/2, and you takes a function to transform your data.

2 Likes

You can even simplify with

iex> Enum.reduce [1, 2, 3], & Kernel.+/2
6

The idea is to pass the implementation (a function) as a parameter.

1 Like

Well, his question was, why do we use Enum.reduce/3 rather than writing this:

def sum([]), do: 0
def sum([h|t]), do: h + sum(t)

Or its tail recursive version:

def sum(list), when is_list(list), do: sum(list, 0)

def sum([], sum), do: sum
def sum([h|t], sum), do: sum(t, sum + h)
6 Likes

Unfortunately it’s in Erlang, but I like the description of map & reduce (map & fold in Erlang) here: https://learnyousomeerlang.com/higher-order-functions#get-functional - you get a full step through of how the abstractions were developed using a few examples. The previous chapter on recursion is pretty good too. And there’s an intro to OTP.

3 Likes

Hi @janxgeist,
I would not worry too much about finding it difficult at first. That’s the sign that you are delving into a whole different way to think, and that’s exactly the kind of stuff that levels you up, as it gives you the power to think about the same problem in different ways.

Learning something radically different is not just about absorbing information. It is about developing knowledge, and takes exercise and hands-on experience. Like learning to play an instrument or speak a language: reading about it is not enough, you have to train, spend time on it, and at the beginning it is clumsy and not pleasant, but each day you get better, until you master it and take pleasure in it.

You are probably familiar with other styles of programming, and each of them represents a way to look at computation. Imperative programming is about giving sequential instructions, like a receipt: do this, then do that, then if X do Y. Object oriented programming focuses on “objects” as the subject of our narrative: object X can do this and that, and changes its own state. I actually remember, back in studies, struggling a LOT with understanding what an object even is… reading over and over stuff like “objects group together state and behavior”, understanding the words without actually seeing the meaning of the sentence.

Functional programming is centered on data, and how to transform it. I know that this sentence will clarify exactly nothing, until after enough exercise it will just click. The thing is, we need to unlearn a lot before we can rewire into a different paradigm. Encapsulation, mutability, inheritance, etc. are all concepts that we need to abandon for a bit, and trust that we will come out with newfound knowledge.

A small practical example of centering our thought process on data, starting from your question:

It is basically the same, but it depends on what you center your narrative on: with imperative iteration you think mainly about what action you want to perform, in functional transformations you think about the data you have and the data you want.

In practice, when the data you start with is a collection, you can ask yourself what kind of data should the result be, and choose accordingly:

  • Another collection of the same size, but with different element? Use map. Map goes to a collection of N elements to another collection of N different elements, obtained by transforming the original ones one by one.

  • The same collection, with some elements excluded? Use filter. Filter goes from a collection of N elements to a collection of M < N of the same elements, excluding some of them.

  • Something different, like an aggregate result? Use reduce. Reduce goes to a collection to… whatever you want :slight_smile: In fact, map, filter, etc. could be implemented with reduce.

In all these cases, the subject you are dealing with is your data. Data goes through functions, which return other new data (without changing the original).

In any case, the difficulty you are facing is not specific to you. I personally can relate fully to that, in my case it happened when I learned LISP coming from OOP. It wasn’t easy, but it was worth. Over time, it just started feeling natural, and I feel it expanded my mind right because it was so foreign to me at the start. And what’s great is that being able to think in different ways made me a better software engineer even when using OOP languages.

16 Likes

Ok that makes sense - the concept that some things in fp (like reduce) are just different representations of things that already exist in other languages, but that are implemented in a different way, to shift focus from ‘what to preform’ to ‘what data do I have and what data do I want’.

6 Likes

I’m sorry about your struggle, but I gotta say that it made me feel a lot better.

I’m a 10-year-oop-developer as well, I’ve worked with Java, PHP, Javascript, C#… you name it. But nothing has been as challenging as Elixir is right now.

I’m following books, watching videos, attending meetups and this new world just amazes me. But it’s just hard to leave behind everything you know to learn something brand new.

4 Likes

I think you might be a lot like me. I looked at phoenix and noped the hell out of it (at first). And that was the right thing (for me). Phoenix has a way of doing things, and somewhat expects you to know where things are, and makes assumptions about how you’re using the web.

Are you having difficulty knowing “What you absolutely need to understand”/“what can you copypasta”? There’s a lot of stuff (things in config, everything in the MyApp.Application module) that you don’t need to know to start understanding the language, but which you WILL need when you want to write a program that can run reliably in prod. I have been doing elixir for a while now and a few of those things are still a bit of a mystery to me… But I think elixir can be scary because it doesn’t really hide those detailey things from you like other languages and platforms do.

I also struggled a lot with Elixir and must confess, that I still struggle with it.

One thing that really helped me getting more confortable writing Elixir code was doing some code katas, and reading other people’s solutions: You will quickly be introduced to a wide set of functions and have a better understanding of the different ways you could solve one given problem (and chose the one that fits you the best).

After having completed some exercises, I find it way easier for me to express myself in Elixir; the language is not so much of a barrier anymore.

Take a look at:

4 Likes

This is not an answer but a shared sense of struggle.

For each language learned, I find it difficult to get the same level of productivity as the previous language I was more familiar with until I get more at-bats and it starts to sink in. And it’s been like that for the past 30+ years.

Similarly, learning to read music after playing an instrument for years is frustrating because you just want to play and sight reading is so slow when you are just beginning.

Both are like Goethe says: “Every thing is hard until it’s easy”.

5 Likes

Folks have already recommended a lot of places where you can find useful exercises and problems. Another one that I’ve found valuable is the yearly Advent of Code series. You won’t get any leaderboard position in April, but the problems are hard enough to be head-scratching but not so difficult they require months of work.

My favorite part of the problems, though, is the structure. Each one presents a “part 1” where the solution is practical with pretty much any approach - then there’s typically a “part 2” (only visible AFTER submitting a correct answer for part 1) that either significantly expands the size of the problem or adds additional restrictions / caveats. Ways this proves valuable:

  • expanding the size can surface poor-performing algorithms. For instance, an algorithm that involves appending to the end of a list in Elixir will do OK with 100 items but take eternity for 100_000.
    For one problem, I ended up writing a doubly-linked circular list in ETS to get acceptable performance inserting values in the middle of a list…

  • adding restrictions gives you feedback about the flexibility of your part1 design: do the restrictions require a rewrite, or are there logical extension points in the code? That’s not usually the sort of thing you find out without waiting six months at work.

In recent years, some of the problems will depend on previous problems, providing even more design feedback. For instance this year featured a series of challenges building an interpreter for a machine language, culminating in simulating a network of those machines that does babby’s first DHCP…

4 Likes

I find the reason why it confuses so many new comers to the language is people jump too fast to a framework or a solution, before really understand what’s going on with this language.

I’ve read lots of books in Elixir/Erlang.

  • Programming Elixir by Dave Thomas
  • Elixir in Action by Saša Jurić
  • The little Elixir OTP book by Benjamin Tan Wei Hao
  • Programming Erlang by Joe Armstrong
  • Designing for Scalability with Erlang/OTP by Francesco Cesarini

They are all great books! But If only choose one for beginner, I’d choose Elixir in Action by Saša Jurić. I find it fills the gap and tell you why this language works this way, instead of showing you a bunch of examples that how to do stuff in this language.

Once you understand the why part, the how part will come naturally for you.

Good luck!

9 Likes