If you could change one thing in Elixir language, what you would change?

syntax
core
learning-elixir

#43

I would mandate that Liveview was to be open to the public from the very first alpha version!


#44

By namespace you mean anonymous or declared? If yes:

  1. The compiler runs faster.
  2. Investigating the origin and implementation of it is easier.
  3. Explicit over implicit.

On the other hand, the only argument I see for anonymous functions not to have “.” on calls is just that it is ugly.


#45

For compiler there is no ambiguity as all macros names are stored in compile-time ETS table, so the compiler knows what is macro and what is function call.


#46

Yep, I know, the thing is, everytime there is a call, it should check first if it is a macro to then fallback to the function list. If macro calling where different this double check would not be needed.

I like the idea. Maybe that’s something that could be done in elixir with another delimiter (like $) at the end to characterize macros, or even at the beginning. But maybe enforce that by warnings instead of forcing it with compile errors, because some macros would feel just too strange with that, like $if or if$ instead of if, which feels like the most languages out there, and that’s one of the advantages of Elixir.

Anyway, I have no expectations that will ever get added to Elixir, but I’m starting to think the idea is awesome. :joy:


#47

Is having 2 namespaces rather than 1 namespace more explicit?

In Gleam I’ve unified local and module functions into a single namespace and I don’t feel anything was lost, and it’s trivial for the compiler to track which Erlang namespace to use. It certainly doesn’t run any slower as a result of keeping track of this.

It’s not more difficult to implement a “go to definition” tooling feature with either approach, so I’m not sure I’m convinced that investigating the implementation is easier with 2 namespaces.


#48

Erlang already has a syntax for this, the ? prefix. i.e. ?assert(Value). I like the idea of keeping close to Erlang where possible


#49

Long time reader, first time poster, and by no means an expert, but I’d like to have something that amounts to a a long running “Task”.

I suppose what I’m thinking of is essentially a Genserver, but sometimes, the idea of writing and wiring up a Genserver for (as an example) a persistent DB connect, feels like overkill.

What I’m thinking of, is probably an Agent (facepalm)…

But if that’s the case, I’d suggest that the Agent module probably needs a name change, or more explicit documentation and some better examples.


#50

Didn’t know about the ? prefix on erlang, Not sure if that’s possible though, since ? prefix is already used for chars:

iex(1)> ?a
97

I was actually not even aware erlang had macros, does it?


#51

So GenServer is the most generic long running server you have available, so it’s understandable it’s not very focused and might need boilerplate for some tasks. Agent and Tasks are more specialized processes, where Agent is for keeping data around and Tasks are for doing stuff, which does not require sticking around for longer than the task needs. There are also specialized processes from 3rd parties like GenStage or DBConnection. So if you have a need for a more specialized server you could build it on top of a generic genserver.


#52

Test public functions. Yeah I know it is not supposed to be tested but you end up messing with TDD and forces what could be a single public function into multiple-ones in order to have them tested. Moving that to a separated module just to test them without exposing to the API is very boring and very repetitive.


#53

You mean private right?

I do actually like this because it forces me to separate the public API from the private one by have a module for each one. A feature related to that I’m eager to see is the @module_visible_to discussed here: Proposal: Private modules (general discussion)


#54

Yes, Private!

The problem is when you only have to do simple tasks.

Imagine that you have are writing an adapter that needs to requests the CSV, parse and then transform one of the columns that have a string like “Technology > Programming > Elixir” into a nested map.

Your adapter only need to have a single public function call like “fetch_categories/0” but for testing (mainly TDD) you want to test each piece in isolation. So you end-up needing to create a separated module just to test those functions even if they were never supposedly to be reused. The compiler could just ignore the privacy in the test environment and made life easier.


#55

I think that EUnit approach there is quite good one, where you can define test functions within module itself, so you have possibility to call all private functions and unit-test them, ex.:

-module(foo).

-export([foo/1]).

foo(Num) -> bar(Num, 5).

bar(A, B) -> A + B.

-ifdef(TEST).

-include_lib("eunit/include/eunit.hrl").
-include_lib("kernel/include/assertions.hrl").

bar_test() ->
  ?assertEqual(2, bar(1, 1)).

-endif.

#56

Pretty interesting! I saw some people doing this on other languages, and you’re right, it totally works to test private funcs. But I think it also could be implemented as a library, maybe I take some time to do a PoC on that! :smiley:


#57

That’s more similar to C preprocessor macro’s then AST macro’s though. Erlang’s form of AST macro’s are parse-transforms, which don’t require any prefix or suffix (and, well, they can arbitrarily change any ast form).

C Preprocessor style macro’s, not lispy/elixiry macros.

A common pattern I did in Erlang was to add a test function to each module to test internal things, and that would be called by the main test scripts. The test function was not compiled in at all in a non-testing environment. I actually quite liked the tests being in-file with the code.

Yep, like that.


#58

Done PoC it provides one macro unit_tests that allows you to define tests that are available only when Mix.env() == :test (however this is configurable).

Example from README:

defmodule Foo do
  import ExUnitEmbedded

  defp foo, do: :ok

  unit_tests do
    test "foo/0 returns :ok" do
      assert :ok = foo()
    end
  end
end

Then in your test module invoke Foo.ex_unit_register/0:

defmodule FooTest do
  use ExUnit.Case

  Foo.ex_unit_register()
end

Changed API a little bit:

defmodule Foo do
  use ExUnitEmbedded

  defp foo, do: :ok

  test "foo/0 returns :ok" do
    assert :ok = foo()
  end
end

Then in your test module invoke unittest Foo:

defmodule FooTest do
  use ExUnit.Case
  import ExUnitEmbedded

  unittest ExUnitEmbedded
end

#59

Your adapter only need to have a single public function call like “fetch_categories/0” but for testing (mainly TDD) you want to test each piece in isolation. So you end-up needing to create a separated module just to test those functions even if they were never supposedly to be reused. The compiler could just ignore the privacy in the test environment and made life easier.

https://github.com/wistia/ex_ex includes a macro called defp_testable which expands to def if the Mix env is :test, and defp otherwise. Maybe my “one thing” would be changing the standard definition of defp to act like this. :smile:


#60

Technically macros and functions are different, syntactically they are the same, otherwise the parse would have to know which is a macro and which is a function.

The best solution is to always have parentheses but a have a better syntax for using them in both macros and functions. A better syntax like lisp. :wink:


#61

I wish we could define any operator we want


#62

If you haven’t seen the recent updates to Dialyxir via transformations of the error messages by my Erlex library, I encourage you to seek them out. We now control all error messages, so any PRs to address deficiencies in language should be straightforward to add and not require any Erlang knowledge. If you have an editor’s hand, please consider doing so =)

If it fails to transform the message, it falls back to the Erlang output, so there’s no reason to not use it, if you’re already using Dialyzer.

Additionally, there’s a --explain flag that allows you to see what a particular error class would mean. We do not get enough information from Dialyzer currently to be able to give you an explanation of the specific error, but we can tell you what no_return means and give an example of what would cause that type of error. Warnings are listable by calling Dialyxir.Warnings.warnings/0.