Live data from Hacker News

Comparing Elixir and Go

blog.codeship.com

121–130 of 202 posts

Re: Comparing Elixir and Go

#121
post #99

Earlier quoted context omitted.

It is possible to start external processes from BEAM and interact with them. I've blogged a bit about it at http://theerlangelist.com/article/outside_elixir You can also write NIFs (native implemented functions) which run in BEAM process (see http://andrealeopardi.com/posts/using-c-from-elixir-with-nif... ). The latter option should be the last resort though, because it can violate safety guarantees of BEAM, in parti…

Love your blog and book Sasa. Could elaborate on the fair scheduling disruption by NIFs? Don't recall ever reading about that

Thanks, nice to hear that!

Basically a NIF blocks the scheduler, so if you run a tight loop for a long time, there will be no preemption. Therefore, invoking foo(), where a foo is a NIF which runs for say 10 seconds, means a single process will get 10 seconds of uninterrupted scheduler time, which is way more than other processes not calling that NIF.

There are ways of addressing that (called dirty schedulers), but the thing is that you need to be aware of the issue in the first place.

If due to some bug a NIF implementation ends up in an infinite loop, then the scheduler will be blocked forever, and the only way to fix it is to restart the whole system. That is btw. a property of all cooperative schedulers, so it can happen in Go as well.

In contrast, if you're not using NIFs, I can't think of any Erlang/Elixir program that will block the scheduler forever, and assuming I'm right, that problem is completely off the table.

Re: Comparing Elixir and Go

#122
post #99

Earlier quoted context omitted.

It is possible to start external processes from BEAM and interact with them. I've blogged a bit about it at http://theerlangelist.com/article/outside_elixir You can also write NIFs (native implemented functions) which run in BEAM process (see http://andrealeopardi.com/posts/using-c-from-elixir-with-nif... ). The latter option should be the last resort though, because it can violate safety guarantees of BEAM, in parti…

I spent 30 minutes looking at NIF, but I was scared away. My understanding is that if the NIF crashes then BEAM crashes. Which leads me to think that if you need NIF then you need safety guarantees on the Native side that C can't provide.

Precisely, which is why I always advise to consider ports first :-)

However, in some situations the overhead of communicating with a port might be too large, so then you have two options:

  1. Move more code to another language which you run as a port.
  2. Use a NIF
It's hard to generalize, but I'd likely consider option 1 first.

If you go for a NIF, you can try to keep its code as simple as possible which should reduce the chances of crashing. You can also consider extracting out the minimum BEAM part which uses the NIF into a separate BEAM node which runs on the same machine. That will reduce the failure surface if the NIF crashes.

I've also seen people implementing NIFs in Rust for better safety, so that's another option to consider.

So there are a lot of options, but as I said, NIF would usually be my last choice precisely for the reason you mention :-)

Re: Comparing Elixir and Go

#123
post #96

Earlier quoted context omitted.

> Sometimes mutations are useful especially in a hello-world sized projects. as you scale up mutations become a constant source of bugs, bottlenecks and complexity. > always copying objects by value every time you call a function seems very expensive; especially if you're dealing with very large objects/structs/maps/strings which have to be processed by many functions structural sharing in functional persistent data…

I think the person's point was that you sacrifice performance - having the ability to mutate is only a good thing there, as immutability without being able to exit out of that is too inflexible and potentially could cause other hacks to appear when trying to solve that problem. FP with a focus on immutability is good for the default, but ignoring its limitations is bad.

Sure, but I don't know any pure languages that provide no facilities for mutation whatsoever, they just ask you to be explicit about it and add some syntactic weight (which is a good thing, you want mutation called out as a reader). Haskell provides several different mutation options from simple references to various kinds of thread-safe references depending on what semantics you need. There's even a "no-really-trust-me" option.

So where does the myth that you can't exit out of immutability come from?

Re: Comparing Elixir and Go

#124

Author here. This was published a week earlier than expected so just a heads up that there are a couple of edits coming.

ZeroMQ is a library written in C++, not a server written in Erlang.

Working on getting it removed. That was my mistake.

Re: Comparing Elixir and Go

#125
The article states that Elixir functions must be in a module, during its comparison between Goroutines and one of the few methods of using concurrency in Elixir (there are others beside spawn). This description isn't entirely accurate. Named functions must be inside a module in Elixir, but anonymous functions don't have that requirement.

Re: Comparing Elixir and Go

#126

Earlier quoted context omitted.

Maybe already fixed, but here are a couple things I noticed: Go methods have to be defined in the same package as their receiver's type. As a result, you can only add methods to your own types, not someone else's. Also, the article claims that Go interfaces are similar to Elixir's pattern matching but I don't see a resemblance. Perhaps clarify that?

But you can define an interface that matches someone else's types and then attach the method to it. The main similarity I was going for was the way that an interface fits anything that matches it, rather than being directly implemented by the type. A method could be attached to an interface that matches a struct with two specific variables while Elixir could define a pattern on a function that worked for any struct t…

The confusion with GP might be that you can't, in fact, define an interface and attach methods to it. More info @ https://golang.org/ref/spec#Method_declarations

Re: Comparing Elixir and Go

#127

Earlier quoted context omitted.

Love your blog and book Sasa. Could elaborate on the fair scheduling disruption by NIFs? Don't recall ever reading about that

Thanks, nice to hear that! Basically a NIF blocks the scheduler, so if you run a tight loop for a long time, there will be no preemption. Therefore, invoking foo(), where a foo is a NIF which runs for say 10 seconds, means a single process will get 10 seconds of uninterrupted scheduler time, which is way more than other processes not calling that NIF. There are ways of addressing that (called dirty schedulers), but t…

As linked elsewhere here, tight loops that never preempt are being fixed in Go 1.8/1.9[0]. Looks like a flag may been added to Go 1.8 called "GOEXPERIMENT=preemptibleloops" that adds a preemptible point at the end of a loop. It's behind a flag for performance/testing reasons, but they are working on it.

[0] https://github.com/golang/go/issues/10958

Re: Comparing Elixir and Go

#128
post #96

Earlier quoted context omitted.

> Sometimes mutations are useful especially in a hello-world sized projects. as you scale up mutations become a constant source of bugs, bottlenecks and complexity. > always copying objects by value every time you call a function seems very expensive; especially if you're dealing with very large objects/structs/maps/strings which have to be processed by many functions structural sharing in functional persistent data…

I think the person's point was that you sacrifice performance - having the ability to mutate is only a good thing there, as immutability without being able to exit out of that is too inflexible and potentially could cause other hacks to appear when trying to solve that problem. FP with a focus on immutability is good for the default, but ignoring its limitations is bad.

> having the ability to mutate is only a good thing there

hard to discuss without specific examples, but it is provably bad thing whenever you share the value that you're mutating.

if your problem boils down to crunching numbers, maybe even in a couple threads without needing to communicate and cooperate between them - sure, mutability is hands down best approach, but in other 99.9999% of problems it is a hindrance.

Re: Comparing Elixir and Go

#129

Earlier quoted context omitted.

But suitability is a huge factor here. The point isn't that "nine nines" reliability cannot be achieved in C++ or whatever, it's that it's significantly harder and costlier to achieve. But also: Of course a language creates reliability. Null pointers are impossible in Erlang, whereas they are provable impossible to avoid in C++. So people bring this up because Erlang programs are pretty much reliable by default (assu…

> Null pointers are impossible in Erlang, whereas they are provable impossible to avoid in C++. They are trivially possible to avoid in C++ as you do not have to use raw pointers. References, value types, custom non-null smart_ptrs, etc.

Not trivially, no. Even if you try to avoid "raw pointers", C++ is an unsafe language by design, and it's non-trivial to turn it into a verifiably safe one:

* Even smart pointers are still pointers, which are trivial to access after release by mistake; and references are only safe as long as the caller still exists. C++ does not have anything like Rust's borrow checker.

* C++ threads allow race conditions that can screw up even smart pointers.

* Plenty of libraries (C and C++), which are usually impossible to avoid and completely impractical to vet, will still use raw pointers.

Generally, the word "trivial" and C++ don't go well together.

Re: Comparing Elixir and Go

#130
post #81

Earlier quoted context omitted.

So now compilers should only be written, if v1.0 is production ready?! > So this is mostly theoretical. No, it is setting the facts straight about the widespread ignorance of mixing languages with implementations.

> So now compilers should only be written, if v1.0 is production ready?! No, but they are only relevant for the purposes of the discussion, that is, when considering whether to adopt a language platform based on if it's AOT or interpreted etc, when they are v1.0. That somebody can always make an interpreter for an AOT language, for example, is nothing people care about when checking whether to use a language for thei…

Turning on cynic mode, I would say the only thing that matters is which company backs a programming language and only languages offered on their OS SDKs are relevant.

Anything else will just add entropy and development costs to projects, due to 2nd class tooling and lack of libraries on the target platform.

Then it doesn't matter how the code gets compiled at all.

Post reply on HN