Get the top HN stories in your inbox every day.
tekacs
tekacs
I'll also leave these here, in case the conversation inevitably turns to pattern-matched function definition in Elixir. :)
https://github.com/clojure/core.match -- pattern matching as a library
https://github.com/killme2008/defun -- using core.match to implement Elixir/Erlang-like function definition
https://github.com/noprompt/meander -- advanced pattern matching, for some fun and flavor :)
What's also fun is that core.match is implemented using a paper from INRIA on how to efficiently convert patterns into decision trees:
https://github.com/clojure/core.match/wiki/Understanding-the...
SatvikBeri
If videos are more your speed, David Nolen, the primary author of that library (and article) has a talk about it as well: https://www.youtube.com/watch?v=TVJa-V6U-XI
jwhitlark
I've always loved that paper. Very readable, and gives good insights.
didibus
Elixir's Protocols actually were inspired by Clojure Protocols.
Here's Jose Valim talking about it:
> I’ve learned a lot also from Clojure because, at the time I was thinking about Elixir, Clojure was already around. I like to say it’s one of the top three influences in Elixir
[...]
> The main, the top three influences are Erlang, Ruby, and Clojure.
[...]
> I was like, no, but I’m going to call them protocols because there are a lot of similarities between Clojure and Elixir in terms of them being dynamic languages and in terms of the macro system. I was like, okay, I’m going to call them protocols because the closest thing we have today to what I want is Clojure
And he goes on talking about more inspiration and similarities from Clojure like Agents, etc.
Full exert is here: http://blog.cognitect.com/cognicast/120
kopos
Having worked with both to create the same system (building a game server) I've found Clojure actually sits better with the functional thinking style (1 data structure, 100 functions).
While Phoenix was the killer app for Elixir, and Elixir has far superior readability (using the Ruby syntax); there were couple of things that were off-putting and I struggled with them.
1. everything is inside a module was an unnecessary distraction
2. And then the separation between anonymous and named functions simply were unnecessary
3. And that I would have to declare the data / record inside a module (??)
Elixir felt like a functional language un-necessarily trying to look like a class based language.
I sometimes feel that had Elixir had only supported functions outside of modules... oh that freedom.
But some of the thought that went into Flow, Channels (which has become the de riguere now), mix (developer ergonomics ftw), those micro-second latency responses, distillery are still too classy and amazing.
jeremyjh
> Elixir felt like a functional language un-necessarily trying to look like a class based language.
Not really. Named functions live in modules because that is how it is done in Erlang; Elixir is compiled to Erlang's abstract syntax. In practice does anyone define Clojure functions outside of a namespace? Haskell functions are always defined in modules too, do you think they got that from class based languages?
divs1210
The problem here is that its inconvinient to define a function at the Elixir REPL because it demands that it be put inside a module.
Clojure REPLS allow switching the namespace, and anything you define goes intoo the current namespace. So it is more ergonomic.
tomjakubowski
IEx will let you define a module at the REPL. Combine that with a macro (exercise for reader) in your .iex.exs which expands a function into a module definition and imports it and you're in business.
iex(1)> defmodule Foo, do: def bar(x), do: x + 1
{:module, Foo,
<<70, 79, 82, 49, 0, 0, 4, 192, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 135,
0, 0, 0, 15, 10, 69, 108, 105, 120, 105, 114, 46, 70, 111, 111, 8, 95, 95,
105, 110, 102, 111, 95, 95, 10, 97, 116, ...>>, {:bar, 1}}
iex(2)> Foo.bar(1)
2
iex(3)> import Foo
Foo
iex(4)> bar(1)
2kopos
True. The heritage of erlang is undeniable here.
In Clojure too, the fns, records everything live inside a namespace but that is a modularity mechanism for code organisation.
sergiomattei
I had most of these concerns when I was early learning the language. I found it annoying to have everything in modules. Now, however, I've come to appreciate the organization and structure that this forces upon the programmer.
It makes me structure my code and group related concerns at time of writing. I now code my functions as a working collective rather than individual items.
And with .exs files, you can have multiple modules in one file for quick scripting.[0]
[0]https://github.com/matteing/stack/blob/main/server/boilerpla...
brightball
This was one of my big impressions of Elixir. It forces you into so many things that improve long term maintainability.
kopos
Which is exactly what class based modularity provides
winrid
Sounds like you would love Nim.
You can just ignore modules/namespaces. While you have some procedures private to the file, you can expose them and they're just in the global namespace.
elcritch
Maybe my enjoyment with Nim is partly due to the (awesome!) boringness of Elixir at times. I write Elixir code. It mostly just works with a few simple abstractions. Nim's more fun for MCU's and fast code where allocations count and I don't care about scalability of the application as much. I rather enjoy both.
divs1210
I have had the same experience!
The distinction b/w anonymous and named functions is especially icky.
I also agree that Elixir leads to more readable code, lots of people in the Clojure community tend to write dense one liners to appear "cool".
IggleSniggle
I have a feeling it’s not to appear “cool,” but because when you’re writing code there is a tendency to prefer higher density.
Why?
Because higher density makes it easier to see the entirety of a context: more code on the screen makes it easier to spot the relationships.
This happens in other languages too, of course, but it’s easy to see why Closure pushes flow in that direction.
dmitriid
I've seen inscrutable one-liners with Elixir's capture syntax, too (and written some myself :) )
sandbags
Like others all of my Clojure code was in namespaces so I see it as a wash between the two. In practice I cannot imagine any real app not making use of such modularity.
I too found the "." syntax for anonymous functions a bit jarring at first. Why treat them differently? In practice I don't even notice it now. It's never been confusing, it's just a wart.
Also, I found the one-struct-per-module thing a bit odd to begin with but in practice it makes a lot of sense. Also since you can put modules inside modules it's no encumberance if you want to declare a number of related structs. Again, once I was used to it I apprecitated the simplicity.
I disagree with your characterisation: I perceive no "class-based"'ness about Elixir. Do you have some examples? Perhaps there is somethign I have missed. So far, given that it's not exposing a class based system underneath, Elixir has seemed even further from this than Clojure.
And I have, in general, found the tooling support friendlier for Elixir. The only thing that I really gripe about is the inability to communicate between editor and REPL.
kopos
My “impression” of class based comes from the usage that defrecord was to be inside a defmodule only; not as free floating records.
About the tooling definitely yes. Elixir is pretty pretty good; at compile, build and employ time.
Aboh33
I recently found there was a clojure implementation for .NET and also one for the BEAM Virtual Machine. Has anyone used the latter? Regards
[1] https://github.com/clojure/clojure-clr [2] https://github.com/clojerl/clojerl
AkshitGarg
There is also lfe [1] which is a lisp for the BEAM VM
[1] https://lfe.io/
didibus
Why do you say "was"? They are both still actively developed even if not very popular.
zcam
There is also ClojureDart in the making. That one seems quite promising.
dnautics
You can, you can match against the module in the struct of the second parameter, concatenate the two modules, and manually dispatch against the concatenated module. I don't really recommend this (it is not performant and feels like a code smell), but it is possible
dnautics
defmodule A do
defstruct [...]
end
defmodule B do
defstruct [...]
end
defprotocol C do
def foo(a, b)
end
defimpl C, for: Any do
def foo(x1 = %m1{}, x2 = %m2{})
Module.concat(m1, m2).foo(x1, x2)
end
end
defmodule A.A do
def foo...
end
defmodule A.B do
def foo...blunte
Does performance matter considering Elixir is already very slow in per-thread performance?
Zababa
On the web site of things, it is faster than Rails, Django, Laravel, Express and even Spring, and this is with Plug and Ecto. https://www.techempower.com/benchmarks/. That seems to be very good performance to me.
blunte
People go nuts here on HN if they think you said something bad about their baby.
I said per-thread, but I meant single-thread... as in CPU bound single process activity. That is undisputedly slow compared to languages like Java, C/C++, Go, etc. That is also very much not what Erlang is designed for. It adds a lot of overhead with the supervisor and other features which do not give a benefit for single thread CPU heavy activities.
And since the parent poster mentioned the multi-dispatch approach would not be performant, I attempted to suggest that the performance cost of that would be less relevant considering Elixir is already not very performant in single thread cases. In other words, it was a moot point.
I never said that Elixir/Erlang was slow for multi-thread/process distributed activities. Obviously that is where it really excels. But if you want to crunch numbers sequentially in a way that cannot be spread across multiple processes/threads, then you will find Elixir to be slow.
The benchmarks you are referring to are very much multi-thread comparisons. They are specifically NOT what I was was slow.
dnautics
depends on what you are doing. And it's not necessarily "very slow", moving forward as the JIT gets better and better. I highly doubt that this technique is going to be very jittable.
brightball
How is it slow?
blunte
How? Do you mean how do I know it's slow? Because it takes longer to run.
Write a typical computation such as Fibonacci in Java and Erlang/Elixir and compare. Fortunately someone has already done this.
Elixir is 3x slower than C and 2x slower than Java for this single thread example.
https://github.com/drujensen/fib
Apparently this upsets people for me to point this out. However, I did not say that Elixir was slow in general or a bad choice. It's an excellent choice for problems which suit parallelization or which require reliable, consistent performance.
Since the parent poster had commented that adding this multi-module dispatch would not be performant, I merely pointed out that the single thread peformance was already slow (as in, why worry too much about the performance cost of the multi dispatch suggestion).
ollysb
https://github.com/OvermindDL1/protocol_ex gives you protocols with full pattern matching.
sandbags
Brilliant, thank you.
I assumed that, with Elixir macro support, someone could implement protocols with full pattern matching. Just way above my current pay grade!
tobyhinloopen
Using dashes in function names? Blasphemy
nlitened
That’s called “kebab case”. When your language supports it, you know the language is good :)
sandbags
Funky, I hadn't even noticed I had switched back when I started thinking about Clojure and that it's not legal Elixir. I had to go check my Elixir code to find the _'s!
rainygold
Feels like Elixir has stolen much of Clojure's appeal and 'thunder' as the niche pragmatic functional language.
arvidkahl
The ruby-like syntax and the VERY open and newbie-friendly community have definitely contributed to this.
I've been to Elixir conferences, and they felt like people were just encouraging each other to build solid software WITH each other. I've not seen this level of camaraderie for other programming language communities.
Elixir devs — and I am super biased here — are a special bunch :)
uDontKnowMe
That's great to hear! I get the same feeling towards the Clojure community as well, some of the friendliest, smartest and most helpful people hanging out at the Clojure watering holes (in comparison to other languages I've worked with). I also am constantly in awe of the output of the Clojure world. There are like 3 or 4 great podcasts going, so many cool projects being worked on, especially for a community which seems to be sadly so small.
filoeleven
Which podcasts do you recommend?
rainygold
Community can definitely play a role though I can't help but think that Phoenix and the proselytizing done by Jose and co are the main factors.
Sure, we have Luminus in the Clojureverse but its just not as easy and straightforward as the Rails-like experience of Phoenix. You don't have Hickey personally responding to comments on HN/Reddit etc.
KingMob
Hickey is extremely remote from the Clojure community. Most of Cognitect is. Really, only Alex Miller engages on a regular basis. To a lesser extent, Fogus and Ghadi do, too.
The Clojurescript community is friendlier, IMO.
didibus
For the most part, Clojure is tuned for experienced developers I think. It's kind of an oximoron, but it feels like it was Rich Hickey's goal as well, to not appeal to any of the "easy" and "convenient" and "familiar", but focus entirely in "no bs", "simple" and "very flexible" pieces that never break backwards compatibility, are always open for extension, keep performance in mind, reaches for battle tested hosts when it can, and all that.
didibus
There's a big downside to Clojure having the most amount of experienced developers I think in bringing beginners in. You'd think it be the opposite, but beginners are better treated by other passionate smart beginners and people who just got out of being a beginner. As experienced old devs tend to not have the time or patience or not know how to explain things or make it beginner friendly.
cutler
Come on, every language community says that about itself. Rust is the latest "amazing" community.
sandbags
Honestly I wouldn't be using Elixir if it wasn't for my co-founder's singing the praises of Phoenix & Liveview. I'd tried it once about a year ago but couldn't see any advantage over Clojure (esp. with re-frame). Sadly he was never going to move to Clojure.
That said, now I've dug in and really used it for solving some problems I am finding it an elegant and enjoyable experience.
I'd happily use either although I think Elixir has a particular fit to web applications.
dgb23
Elixir seems to be very approachable. But some of the strengths of Clojure are unmatched, such as Java/JS interop, isomorphic code for web development, and generally being a Lisp (which includes macros).
Zababa
Elixir has macros. People are currently building Nx https://github.com/elixir-nx/nx while not having to change anything in the language. Java/JS interop being unmatched is true, but Elixir has Erlang interop too.
rainygold
LiveView sorta tackles the isomorphic web development side, and while I agree that Lisp is a better syntax you do have hygienic macros in Elixir as well.
nesarkvechnep
Elixir has Erlang interop. Erlang is a 30 year old language.
adamkittelson
Yes and no. Yeah Erlang has been around for 30 years but "30 year old language" makes it sound like it came out 30 years ago and then stopped. Erlang is still being very actively developed and improved, it's by no means a crusty old language.
lawn
Elixir has macros too.
brianberns
To me, F# is the pragmatic functional language with the most appeal, but being on .NET maybe it’s not even niche.
pjmlp
It kind of is, to .NET developers.
Although Microsoft has made it from Microsoft Research into official Visual Studio, it has been mostly an up hill fight to keep it there, while .NET languages group mostly cares about C# and VB, and to certain extent C++/CLI for integration with Windows APIs.
F# when taken into account by management, comes always after those three.
Most of the Visual Studio tooling for C# and VB isn't available to F# projects, you are supposed to do it manually, e.g. GUI designers, EF database to code generation, .NET 5 code generators.
vendiddy
Elixir afaik has taken a lot of inspiration from clojure and lisp type languages.
It's not readily apparent from the ruby like syntax though.
weatherlight
its funny you say that, Ruby ALSO took a lot from Lisp and lisp like langugaes. there's even a built in parser in Ruby that converts the language into S-expressions
require 'ripper'
Ripper.sexp('a && b')
=> [:program, [[:binary, [:vcall, [:@ident, "a", [1, 0]]], :"&&", [:vcall, [:@ident, "b", [1, 5]]]]]]
Ripper.sexp(<<-RB)
if a
b
else
c
end
RB
=> [:program, [[:if, [:vcall, [:@ident, "a", [1, 5]]], [[:vcall, [:@ident, "b", [2, 4]]]], [:else, [[:vcall, [:@ident, "c", [4, 4]]]]]]]]
Ruby is just way denser than most lisps.I love Ruby, Elixir, and Lisp(s)
vendiddy
Didn't know that!
One striking similarly Elixir has to lisp style languages is the macro system. You have quoted and unquoted expressions just like lisp.
And like lisp most of Elixir, down to the low level stuff like if statements, modules, and so on, is built up from a few primitives.
Here are the primitives that the Elixir language is built from:
cutler
Not really. In most job stats I've seen Clojure is ahead of Elixir.
nsonha
protocols are still just single dispatch like interfaces and virtuals in other languages. This person uses a title that makes it seem like Elixir has multiple dispatch, only to conclude at the end that protocols are nothing like multimethods.
dmitriid
It's one of those "rarely used in practice but insanely frustrating when you write a library or some generalized code".
However, I'd rather Clojure got proper pattern-matching than Elixir multimethods. I find pattern matching a much more powerful, flexible and useful tool.
OliverM
Why not just use core.match? https://github.com/clojure/core.match
dmitriid
Nice. Didn't know/remember it existed
Get the top HN stories in your inbox every day.
> Now Elixir has an equivalent to multi, the Protocol.
For what it's worth, Clojure also has a much closer fit to Elixir Protocols called... a Protocol.
https://www.braveclojure.com/multimethods-records-protocols/...
They too can only dispatch on the type of the first argument, but are more structured (you can add multiple pieces of behavior at a time) and performant than multimethods where that's the behavior you're looking for.