Learning Elixir, frst impressions ( plz don't kill me ! )

learning-elixir

#1

About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: )

Hello all, this is my first post in this forum, so I apologize is something is off.
I am rather a direct person with strong opinions, but don’t be afraid of me, I don’t bite !

Recently I have been learning Elixir for a job offer ( yes, apparently you can find jobs using this thing! I mean, you have to sacrifice a virgin and a goat [ it can be a virgin goat ] to the Gods of Old and pray for 7 days, but hey, if it works it works right ? ) and I have been ramping up in my Elixir knowledge by watching as many conferences as possible.

I have a strong JS background where I do FP ( yes, it is possible ), and I got lured into this language becasue of it.

I have a lot of questions so I am giving this forum a chance!

Source

This 1 hour video covers the main aspects of Elixir and makes sure you have a lot of questions after. Suffice to say you won’t learn Elixir in 1 hour, but it is a good start I guess.

The following points summarize what I learned.

Elixir Facts

  • It has modules. Modules contain and organize functions, so it’s cool.
  • It has a ton of data types, most notably: Int, Float, String, Atom, Tuples ( arrays ), Lists, Dictionaries, etc.
  • Int type is not bounded. It has no maximum nor minimum value. If you ever had an overflow using Node ( like me ) you will really appreciate this feature.
  • Has a native pipe operator ! |> JS is being left behind :stuck_out_tongue:
  • Has native String concat operator <>
  • Has native List concat and diff operators ++`` and --
  • Has destructuring !
  • Functions can have the same name as long as their signature is different !
  • Has anonymous functions
  • Has no for loop constructor. WHHHATTATATAGGAHHAHAGATIhvgdjfsAVSGBZ
  • Creates child processes easily and sends messages between them easily as well

Elixir Cons

Not everything is nice with Elixir. It also has it’s technical shortcomings:

  • No partial application and no currying. This is something I would expect from a FP language ( like Haskell ) to natively support. Huge disappointment.
  • Currying and partial application via anonymous functions have a weird syntax. Yes, you can make a library to workaround this flaw, but the syntax you need to use is … weird at best: http://blog.patrikstorm.com/function-currying-in-elixir
  • Being functional, Elixir has an horrible API. Most of it’s native functions are data first, instead of data last ( a basic concept of FP languages ).
  • No Monads. Again very disappointing.

Elixir Weirdness ( aka, stuff you will eventually get used to )

  • No return statements
  • Elixir also has default values for parameters, but the syntax is anything but intuitive. (x // 1, y // 2), this signature is saying that the default value for x is 1 and for y is 2. For most people, this would be a comment…
  • It has a short syntax for functions ( like the arrow syntax for JS functions ) but it is super cryptic.

Overall opinion

I know I am just starting. But aside from the data types ( that are immutable by nature ) JS actually has better support for the functional paradigm than Elixir. The native API ( String API comes to mind ) was designed using data first instead of data last ( a most basic flaw in an FP language ) and using currying with partial application feels clunky as all hell when compared to JS. And there are no Monads either … I mean Swift is not a FP language and even it has the Maybe Monad. How disappointing.

I must say I expected more from a newly designed FP language. The support for currying and partial application is rather atrocious but I can live with it.

I still didn’t get into error handling, but from what I have seen, it is extremely imperative with good old try rescue blocks. It would appear that basic knowledge from Promises and Futures is missing.

Questions

Or perhaps I am missing something? Allow me to rephrase my noob rant in an organized manner:

  1. How does one use Monads in Elixir?
  2. Is it possible to use currying and partial application in Elixir using the function normal syntax, instead of the .(x).(y) syntax?
  3. Are there any libraries widely used and publicly available to for the use of currying and monads?
  4. Are there any libraries that improve upon the mistakes of the native API?
  5. Is there a version of NPM or Ruby Gems for Elixir ( I understand there is none so far ) ?

Please let me know if I missed something. I have only seen a a couple of conference videos, I may very well be missing something brutal.


#2

You mistake Erlang and Elixir as ‘pure’ functional programming languages. They aren’t.

Erlang is not like Haskell.

Erlang was built in a business mindset to serve business goal which has to be achieved by making trade-offs. There are some aspects in functional programming paradigm that perfectly suites with business models they were after (reliability and concurrency). They took it, they used it, there you go, Erlang.

Haskell was built in an academic mindset to be used as academic purposes.


#3

In elixir everything is in a big-IO monad you can’t escape. Elixir is not a pure functional language, but uses side effects all over the place.

What do you mean by “normal syntax”? For me this looks pretty normal for calling into anonymous functions.

Searching on hex.pm for monads gave me a few results, but I’m unsure how widely used they are. https://hex.pm/packages?search=monad&sort=recent_downloads

Which mistakes do you mean? Is it only the subject first thing? Its not a mistake… Its designed to work with the pipe operator, not with function composition, as we do not compose as you’d do it in haskell.

I’m not pretty sure what you mean by this, but I think you are searching for hex-packages: hex.pm

Well, expected errors are usually handled via {:error, reason} tuples you match against.

Exceptions are usually only used for errors you do neither expect, nor could recover from.


#4

It does have list (or rather Enumerable) comprehensions though, you use a range to generate consecutive values:

for i <- 1..n do
  ...
end

#5

I don’t think Elixir ever aspired to be a “hardcore FP language” like Haskell or Purescript. It’s much more similar to e.g. Clojure (doesn’t have automatic currying either) than Haskell.

What Elixir loses in expressiveness and conveninece from not having some of the concepts you mention, it gains back in simplicity. That’s always a balance, and I like where Elixir falls on that scale.


#6

The reason that promises and futures are missing from the language is that the equivalents are part of OTP library. To write large Elixir/Erlang will require you to understand OTP.

The libraries are very consistent and built with the pipe operator in mind.

Erlang grew out of practical business needs. It is functional because of those needs. There are very few languages that permit upgrading a system while it is still running (Erlang, Elixir and Smalltalk).

Have a look at exercism.io for some mentored exercises.


#7

The real benefit of the language is not to be functional, but the ability to spawn million of processes, and still be able to supervise the system.

Because it was made for telecom.


#8

Haskel Programmer:


Elixir Programmer:


#9

with

is kind of monad :slight_smile:

There is also witchcraft


#10

In general, if you are using try/rescue you are probably doing something wrong. Either pattern match or just let the process die and clean it up in the supervisor or somewhere else.

That is not to say you will never use them, they have their uses but in general don’t.

Erlang and Elixir look very different from Haskell This video that I did last year talks about this a bit


#11

Generally Erlang/Elixir uses pattern matching with a return value of {:ok, data} or {:errror, error_message} and you just pattern match on that


#12

Best I can tell every language that works with promises is working super hard to get a built in await function to get back to normal programming, so Elixir may have been ahead of the curve by skipping that.

More to the point, languages with green threads (AKA M:N threading) tend to use that as the core primitive for handling concurrency, isolation, and async code, so the APIs are gonna be different from languages that are built around single threaded event loops.


#13

Maybe you’re missing some videos. For example while not being about Elixir, it’s important about mindset: Simple Made Easy.

Also make a point of watching Thinking like an Erlanger:

I have a strong JS background where I do FP ( yes, it is possible ),

Yes, but the non-FP escape hatches are always too temptingly, too close by, so there is a lot of discipline required to develop a good FP-style and JavaScript being what it is, a pure FP-style isn’t always in the interest of the implementation - e.g. you can use recursion but given that there is no last call optimization, iteration is the right thing to do. So while you can push the limits (JavaScript Allongé, the “Six” Edition), adopting an FP style almost requires that you work in parallel in a true (but not necessarily pure) FP language. So in a JS environment you are better off by getting the most out of transpilation by working in BuckleScript, ReasonML, PureScript, ClojureScript, etc.

Has a native pipe operator ! |> JS is being left behind :stuck_out_tongue:

Keeping in mind that piping is forward function application which can be used as a more pragmatic variation (and some would argue a more readable) variation on function composition.

Has native List concat and diff operators ++ and --

Which really don’t seem as useful as knowing how to appropriately wield construction and pattern matching via the cons cell syntax.

Has destructuring !

More accurately pattern matching - JavaScript only has destructuring. Pattern matching is a conditional construct, destructuring is not.

Functions can have the same name as long as their signature is different !

No they cannot. A single function can be composed of multiple clauses. If it has the same name and the same arity it is the same function. Same name, different arity - different functions. Signature often implies sensitivity to argument type - and pattern matching may give the impression that something like that is going on - but there isn’t.

Has anonymous functions

Which are useful as a closure. If you just need a function then give it an intention revealing name for the sake of clarity. Terseness is not always a benefit.

Has no for loop constructor.

In FP recursion is the name of the game.

No partial application and no currying.

Currying is a programmer convenience but hardly essential. Partial application can be achieved via closures, so no loss there. So it’s not quite as elegant. Haskell is a great learning experience but it’s purity has an elitist flair.

weird syntax.

See above. Syntax is irrelevant and lack of familiarity.

Most of it’s native functions are data first,

Again, given that there is no native support for currying it’s hardly an issue. And Bucklescript is moving the same way (likely to placate ReasonML) and it supports currying.

No Monads

Again there is an elitist element to “purity”. Erlang and Elixir set out to be pragmatic (Beyond Functional Programming with Elixir and Erlang).

No return statements

If it doesn’t return anything it’s not a function, it’s a procedure. And keep in mind that everything is an expression anyway - there are no statements. So by definition a function has to return something, i.e. the value that the last expression evaluates to. So return is extraneous and unnecessary.

For most people, this would be a comment…

Another familarity issue … and only if you come from a C-style language.

is super cryptic.

Syntax is irrelevant. And in this case you can always go with the long hand notation - which you have to use anyway if the function has an arity of 0.

better support for the functional paradigm than Elixir.

Being a functional language has also to do with the features that aren’t supported - namely mutability by default. Furthermore while recursion is possible in JavaScript, it’s not something that is recommended (Bucklescript transpiles recursion to iteration whenever possible).

I must say I expected more from a newly designed FP language.

Well it isn’t. Erlang dates back to 1986. Elixir puts a coat of paint of Ruby-style syntax over it and adds hygenic macro support which is something the LISP community is more familiar with.

it is extremely imperative with good old try rescue blocks

Again you are judging from the perspective of your own familiarity.
Erlang Oddities - Brujo Benavides

It would appear that basic knowledge from Promises and Futures is missing.

Erlang Master Class 2: Video 2 - Abstracting patterns of concurrency


#14

As someone else who just started learning Elixir (coming from a .NET background) I would like to throw my two cents in here.

(Again, just learning elixir, so don’t be too critical)

I think the OP is missing the point a little bit, and as stated by people previously, Elixir (and Erlang alike) was not meant to be a pure functional language, but instead a “Process Oriented Language”, and in this respect it aligns a bit with OOP.

The power of Elixir comes from creating millions of processes that can be distributed across multiple nodes, while supervised by other processes whose sole purpose is to monitor and respawn failed processes. Error handling is not something you do a lot of in Elixir, hence the “Let it fail” way of thinking.

These technologies are a paradigm shift, you have to change the way you think about solving problems before you can fully appreciate it. If you are trying to write Elixir code in the same way you are writing JavaScript code then you are missing the point.


#15

Yes. And you need to realize that promises and futures are awkward ugly workarounds for runtimes that don’t have limited concurrency. They’re not in Erlang/Elixir because they’re not needed.


#16

Very good replies so far, but I’d like to step back for a brief historical overview that might help frame how you view Elixir in relation to other languages you know.

It is fundamentally a “concurrency oriented” language. Joe Armstrong (co-creator of Erlang) has described Erlang as such. Those design forces are reflected deeply in the VM itself, and it’s a property all languages on the BEAM share. The original reason for Erlang’s existence is that Ericsson needed a language to meet specific business needs. Fault tolerance was high on the list. Fault tolerance led to supervision which needed isolated processes (and basically independently discovering the actor model). Isolated processes needed immutable data and first class functions and tail call optimization. Thus functional features fell out of more primordial concerns. All of this pre-dates the creation of Haskell.

If you’re one that thinks proper “functional programming” has to include monads and currying, etc., then mentally put Elixir in a different language category or else you’ll remain frustrated. We have alternative means of solving similar concerns. They’re not as theoretically pure, but in practice work out well.

Everything I’ve said before applies to any language on the BEAM, so now let’s speak to Elixir specifically. It differs from its parent language of Erlang by adding Lisp-style hygienic macros and Clojure-style protocols. It adds the pipe operator from the ML family of languages and intentionally puts the subject first to optimize pipe use. It offers a superficially Ruby-esque syntax in some places (and differs strongly in others). It adds web development style tooling in libraries/packages via hex and in building/compiling via mix. There’s more, but that’s a good first group of things to digest.

There’s more to be said about intangibles like community. I find it quite interesting that you worry enough to put “plz don’t kill me” in your subject, and I’m pleased to see in the replies no inclination to even consider doing that.

Welcome!


#17

Yes the Elixir/Erlang community only kill their children and they use supervisors to do that :grin:.

If you want to compare promises with simple elixir have a look at two of my projects that solve the same problem {:ok, “the Elixir one is not quite finished”}


#18

Wow, so many yummi replies !
This is going to be quite huge !

This truly breaks my heart. There is a huge marketing effort in making Elixir the new cool FP language but it lacks basic FP features because it is not a real FP language, it only borrows some concepts. I am not sure how I feel about the implications of your comment, but it makes sense and is quite unsettling to me.

Normal syntax like when you call a normal function. A JS example:

const add = x => y => x + y;
const increment = add(1);
console.log( increment(2) );
console.log( add(1) (2) );
//so on

In Elixir, when you call a normal function, you don’t call it with the .(). For example:

def add(x, y) do
    x + y
end
# stuff happens
add(1, 2) #instead of add.(1, 2)

Now this is mind blowing. A FP language that doesn’t do composition. Where can I read more about this ( as in, why no composition? a limitation from Erlang ? something else ? ) ?

Correct! Thanks!

Well, expected errors are usually handled via {:error, reason} tuples you match against.

Exceptions are usually only used for errors you do neither expect, nor could recover from.
Ahh, so for Failures ( errors you expect ) you use pattern matching, and for Exceptions ( errors you don’t ) you use the try / rescue construct. I think I get it!

Which is still not a for loop. To be honest, I love the fact Elixir misses a for loop construct! (IMO, a good thing).

Currying, Partial application and Monads are tools. You can choose to use them or not. The problem is for people who want to use those tools, they can’t. So why use Elixir ? You have other very strong FP languages out there with a bigger toolkit to use :smiley:

Quite interesting. Where can i read more about this?
Also, I am already checking Exercism.io :stuck_out_tongue:

I think I get it, very well put!

So you are saying that people usually use the Either Monad, but without realizing it. The issue remains, but as long as I can import a Monads library I won’t mind :stuck_out_tongue:

Now, I would have to disagree here. As someone who has been doing FP in JS for over a year I can tell you there are solutions to this. You may not have Tail Call Optimization implemented, but you can do it yourself in your functions and it’s rather simple once you get the idea. I honestly can’t remember the last I used a ´for loop´ in JS, unless it was for something really performance intensive and synchronous.

A really enjoyable reading. I wouldn’t call it pushing the limits, for me it was merely the start. Purescript and the others are fine, but with vanilla JS and using a couple of libraries you can do pure FP quite easily ( ramda, sanctuary, fluture, folktale, etc ). Those push the limits :stuck_out_tongue:
That’s how I have handled pure FP in JS until now.

This is confusing. A previous user just said Elixir doesn’t really do function composition. I still am quite surprised with this…

Indeed, my comment was simplifying the true capabilities of Elixir. Your post is gold !

def reverse( list ) do: reverse( list, [] ) end
def reverse( [], reversed ), do: reversed
def reverse( [head | tail ], reversed ), do: reverse( tail, [ head | reversed ]) 

Here is an example of 2 functions called reverse, both with the same name and same arity. Now, if I get it correctly, you mean to tell me that this only works because of pattern matching, and that the function’s signature is not being taken into consideration?
I guess I can live with that.

Well, for me its purity keeps people from calling me at 3 AM in the morning because our app crashed with a side effect or worse, got into an inconsistent state because of one. I am not saying Elixir should emulate Haskell ( it was merely an example of a language in my post ) but I do believe Haskell has some things Elixir could really use.

Ahh yes, I am well aware of the familiarity bias. That is not what is stopping me, being familiar with problems empowers you to overcome them. But I do believe that assessing syntax as irrelevant is irresponsible at best. If you don’t believe me, I challenge you to use http://www.jsfuck.com/
:smiley:

In other words, syntax matters :stuck_out_tongue:

My dear, I wouldn’t dare. I have come a long way to know when that is happening :stuck_out_tongue:
That is why I joined this forum with all these wonderful people. I want to learn Elixir. Not to write JS using Elixir.

I completely agree !

Let’s just say that my direct approach and my sense of humor don’t always find the best friends in forums. I don’t insult people and I always try to be respectful and light hearted, but sometime that’s not enough. I really need to get along with this community, so I am just trying to be careful.

Thanks !

Will do!


#19

Now, I would have to disagree here.

My point was that the JavaScript runtime isn’t designed to cope with unbridled recursion regardless of the available tricks. Any for loop avoidance is largely implemented via higher order functions which hide the implementing iterative loop away from you.

using a couple of libraries you can do pure FP

  • those libraries can cheat under the hood (not that it’s necessarily a problem) to align with the sensibilities of the JavaScript runtime
  • and they can’t ensure that your code is pure compared to a compiler

A previous user just said Elixir doesn’t really do function composition.

Not out-of-the-box.

This adheres to our general principle that we do not provide built-in mechanisms; we provide primitives with which mechanisms can be built.

defmodule Demo do

  def f(x) do
    x + 2
  end

  def g(x) do
    x * 3
  end

  # dynamic composition with a closure
  def compose(g,f) do
    fn (x) ->
      x
      |> f.()
      |> g.()
    end
  end

  # "static composition"
  def g_comp_f(x) do
    x
    |> f()
    |> g()
  end

end

x = 5
fun = Demo.compose(&Demo.g/1,&Demo.f/1)

IO.inspect(fun.(x))
IO.inspect(Demo.g_comp_f(x))
$ elixir demo.exs
21
21
$

Here is an example of 2 functions called reverse.

No it is not. Those are two function clauses belonging to the same function. They are functionally equivalent to:

def reverse(list, reversed) do
  case list do
    [] ->
      reversed

    [head|tail] ->
      reverse(tail, [head|reversed])
  end
end

See

I do believe Haskell has some things Elixir could really use.

Why don’t you use Haskell then? There are plenty of forum users here who know Haskell and use it whenever possible but still find Elixir/Erlang useful under many circumstances. I’d personally prefer if there was static typing with a real type system but it’s absence isn’t a showstopper (and I blame JavaScript for getting me used to that fact).

But I do believe that assessing syntax as irrelevant is irresponsible at best.

It’s meant as a “stop whining and get on with the business at hand” notion - and we are talking about Elixir/Erlang here - not JS syntax hampered by backward compatibility considerations.


#20

Reading through this @Fl4m3Ph03n1x your comments and replies really don’t seem to be made in good faith.

You call it a direct approach and your sense of humour but honestly you are just being rude and it is not surprising you find it difficult to make friends on forums with this approach.