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

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

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.

1 Like

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.

1 Like

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:

1 Like

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.

1 Like

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

3 Likes

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.

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?

1 Like

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.

2 Likes

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.

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)

1 Like

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.

1 Like

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.
2 Likes

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:

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.

1 Like

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
5 Likes

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.

GitHub - wistia/ex_ex: A collection of functions that extend the capabilities of Elixir's standard library modules 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:

2 Likes

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:

6 Likes

I wish we could define any operator we want

1 Like

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.

11 Likes