What is `=INFO REPORT====` where is it coming from ? how to surpress it?

Hi !

I am new to Elixir, I am quite confused regarding what is =INFO REPORT==== ? where is it coming from ?

We are talking about a bare mix new kv --module KV.

What does any of the following mean ? Is it necessary at all to display it ?

=INFO REPORT==== 23-Aug-2018::20:12:57.567905 ===
    application: logger
    exited: stopped
    type: temporary
..

I tried disabling it in my mix.exs like so :

def application do
    [
     # extra_applications: [:logger]
    ]
  end

but no luck, I am also unable to find any docs that explains clearly what is going on … any pointers would be much appreciated.

mix test
Compiling 1 file (.ex)
Generated kv app
=INFO REPORT==== 23-Aug-2018::20:12:57.567905 ===
    application: logger
    exited: stopped
    type: temporary
..

Finished in 0.08 seconds
1 doctest, 1 test, 0 failures

cheers !

What is your Erlang/OTP and Elixir versions? You are not supposed to see a report in this case. In any case, this is just an event logged by Erlang.

Hi @josevalim !

This is my Elixir repl :


Erlang/OTP 21 [erts-10.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Interactive Elixir (1.6.5) - press Ctrl+C to exit (type h() ENTER for help)

As you have mentioned, yes I am not running the latest Elixir build - I am sorry about that, I will try to figure out how to get Elixir updated. In the meantime I have a few things to say :

I have watched a lot of your talks and would like to say you are doing a brilliant job !

I do not know how to thank you enough for trying to bring Erlang/OTP to the 21st Century, you must have invested a lot of your time and software needs more people like you.

It’s doubly impressive since your project is competing with the likes of golang which has strong financial backing - so you are arguably winning because of pure merit.

However I am having a tough time understanding Elixir, believe it or not I found Erlang easier to understand and get started compared to Elixir !

A few specific quirks (I hope you do not mind constructive criticism ) :

  use ExUnit.Case
  doctest KV

  test "greets the world" do
    assert KV.hello() == :world
  end

where is test,assert coming from ? is it being imported from ExUnit.Case ?

… but in your own docs it states:

Since use allows any code to run, we can’t really know the side-effects 
of using a module without reading its documentation. For this reason, 
import and alias are often preferred, as their semantics are defined by 
the language.

I strongly agree with this statement - it reminds me of Python’s:

import * from numpy

which we can all agree is not a good idea, it completely threw me off as a beginner.

In nodejs you do not have to learn how to do TDD before trying to figure out how nodejs works, I understand the immense value of TDD and no serious application in 2018 should release without doing TDD - but if you are a beginner its going to be another extra hill you need to climb.

Imagine this - I have worked with C,C++,nodejs,Lua,haskell and its still quite difficult to see how Elixir is working - imagine a first year Computer Science student :frowning: is it not a goal
to show them the beauty of Elixir first ?

For example in nodejs the bare bone project has :

node_modules

package.json

main.js

in Elixir its :

_build
config  
lib  
mix.exs  
README.md  
test

and my code entry point is lib/foo.exs ? How do I know that ? where is it being made explicit ?

A first year CS student wont know the importance of README.md how it relates to git (what is git ?) and how README.md is used by github.com, what is markdown again ?

Imagine if Elixir just had mix.exs and an entry point file made explicit in mix.exs.

Why is config not specified in mix.exs ?

I am sorry, I might just not have enough knowledge regarding why those decisions were made - but I felt I had to let my opinions be know so that Elixir can truly compete with with nodejs in terms of ease of use. I truly want Elixir to take off in a big way.

Cheers !

Looks like output from SASL. You may want to check whether there is anything in your config/config.exs that could be interfering with the default Logger setup (Error logger configuration).

where is test , assert coming from ? is it being imported from ExUnit.Case ?

use injects code.

Hi @peerreynders !

The issue resolved itself after updating Elixir to 1.7.2.

I am sorry for raising this issue.

Cheers !

@peerreynders

Injecting code can be a good thing under certain conditions but arbitrarily adding functions in the global namespace without making it explicit ??

Due to some incompatibilities regarding to how file handling works between OTP 20 and 21, elixir up to 1.6.5 does not work properly with OTP 21. You need to update to 1.6.6 at least.

2 Likes

Why would you think that functions are being added to the global namespace? The used module can create functions (via macros) inside the module use-ing it.

@NobbZ

Thank you for your quick reply.

I should have tried updating my Elixir run-time before raising it as an issue - sorry about that.

@peerreynders

Do you not see how that can be a bad thing ?

Imagine I do :

use ExUnit.Case
use MyExternalTestingModule
  doctest KV

  test "greets the world" do
    assert KV.hello() == :world
  end

MyExternalTestingModule has functions test , describe , assert also defined - its going to result in conflicts. Its always an anti pattern regardless of language. Its even mentioned in the Elixir docs that its not a good idea to use use :

Since use allows any code to run, we can’t really know the side-effects 
of using a module without reading its documentation. For this reason, 
import and alias are often preferred, as their semantics are defined by 
the language

Not following - MyExternalTestingModule essentially also acts as a namespace - so there is no polluting of the global namespace.

and my code entry point is lib/foo.exs ?

mix new kv --module KV

doesn’t even create the equivalent of a main. That is only created with the --sup option and application.ex is the equivalent to a main. But to understand it you need to understand processes and supervisors.

1 Like

What happens if you define two separate namespaces ? who have conflicting names ?

You have to make sure that whatever namespaces you are bringing have no conflicts.

What I mean by global namespaces is the namespace within defmodule KVTest

For note, this alone definitely shows that the beam vm is being though like it’s a normal compiled app, it’s not. :slightly_smiling_face:

There isn’t just one entry point, but rather many, one per loaded application in the vm, and your program can have many applications, whether by you, dependencies you bring in, etc. The beam vm is more like an OS on its own so it is more like you are building a dedicated Linux distribution than a standalone program. :slightly_smiling_face:

1 Like

Elixir makes sure of it for you. If test can come from two places and you invoke it, then it won’t compile and you will have to either pick one or use the fully qualified name.

Elixir is lexically scoped. So every time you have a function that you don’t know from where it comes from, then it has to come from somewhere above in the same file. There is no mutable global namespace per-se (unless you consider the atom table but that’s a separate discussion). There is a global namespace in JS but I think using it is mostly discouraged nowadays too?

I do agree the generated project could be simpler but an actual project does require configuration, documentation and tests, and that’s the direction Mix is pushing you to. It is an important nudge, as I believe not following those practices is more harmful to the community in the long term. If the goal is to learn Elixir per-se, without the project scope, then you can use pure files and IEx, like the Getting Started does.

Some of your questions are also answered in the Mix & OTP guide, which seems to be the one you are following, maybe we could improve the guide somehow? Our hope is that the guide answers everything related to the generated application.

3 Likes

@OvermindDL1

I am sorry but in Erlang you can specify a single entry point, here is a bare example :

-module(app).
-compile(export_all).
run ()->

  io:format("hello world ~n"),

erl -boot start_clean -noshell app run

Lets say I want to create a rough equivalent to

const http = require('http');
const os = require('os');

var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("You've hit " + os.hostname() + "\n");
};

var server = http.createServer(handler);
server.listen(4000);

I start with

$ mix new plug_em --sup

Add these dependencies to plug_em/mix.exs (think adding dependencies to your package.json):

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:cowboy, "~> 2.4"},
      {:plug, "~> 1.6"},
    ]
  end

and fetch them with (kind of like npm fetching the node_modules)

plug_em$ mix deps.get

fiddle with the config/config.exs (entirely optional):

config :logger,
  backends: [:console],
  compile_time_purge_matching: [
    [level_lower_than: :info]
  ]

put some code in lib/plug_em.ex:

defmodule PlugEm do
  require Logger
  import Plug.Conn

  # See plug_em/lib/plug_em/application.ex - children
  # --- BEGIN --- for "service" process
  def child_spec(opts) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [opts]},
      type: :worker,
      restart: :permanent,
      shutdown: 500
    }
  end

  def start_link(_arg),
    do: Plug.Adapters.Cowboy2.http(PlugEm, [])

  # --- END --- for service process


  # --- BEGIN --- part for the (module) Plug spec
  def init(options) do
    options
  end

  def call(conn, _opts) do
    Logger.info(info_text(conn))

    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200,"You've hit \"#{hostname()}\"")
  end
  # --- END --- for the (module) Plug

  def hostname do
    {:ok, hostname} = :inet.gethostname()
    hostname
  end

  def remote_address(conn) do
    case conn.remote_ip do
      {_, _, _, _} = address ->
        address
        |> Tuple.to_list()
        |> Enum.join(".")
      _ ->
        ~S("unknown")
    end
  end

  def path(conn),
    do: conn.request_path

  def info_text(conn),
    do: ~s(Received request from "#{remote_address(conn)}" for "#{path(conn)}")

end

and finally adjust lib\plug_em\application.ex - the “main”:

defmodule PlugEm.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Starts a worker by calling: PlugEm.Worker.start_link(arg)
      # {PlugEm.Worker, arg},
      {PlugEm, []},
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: PlugEm.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

the important bit being:

      {PlugEm, []},

This causes the supervisor to start up a new process which is initialized with PlugEm.start_link/1 - which causes

Plug.Adapters.Cowboy2.http(PlugEm, [])

to run which causes the Plug (as implemented by PlugEm.init/1 and PlugEm.call/2) to be installed to serve requests.

This “application” can be started with

plug_em$ iex -S mix 

and in another terminal:

$ curl http://localhost:4000
You've hit "MBP"$ 
1 Like

You shouldn’t have a single *.exs in your lib folder when writing a mix-project. Well, except you really know what you are doing.

exs files are not compiled by mix at all unless you change the default settings.


In elixirs mix projects you can simply do mix run -e "App.run()", but thats usually for one-offs.

If you do want to do it correctly, you specify mod: {App, []} in your application/0 in the mix.exs. Also you should kick of the creation of a proper supervisiontree in that entrypoint and nothing else.

1 Like

Thanks everybody for all the input ! I appreciate all the help.

I have received a lot of things to mull over, thanks for that !

I hope you can understand the frustration and growing pains a beginner in the Elixir community might face, You guys are already addressing it ! so its quite positive :smile:

Just a final question.

How to do hot reload on file change in Elixir ?

mix compile takes way too long for coding - and we are talking about a bare app ! The only fast solution for Elixir (recompiling in Erlang or nodejs is not a problem since it happens in 1 second for a bare app)
seems to me something that does Erlang’s :

c("./myApp.erl").

but in Elixir, but only on modules that have changed.

cheers !

I have looked at Cortex and test.watch on hex.pm but I couldn’t make Cortex work, it crashes. Is there a built in solution ?

In Elixir’s terminal you can also do recompile (for the whole project), c(file) for a given file and r(module), for a given module.

???

plug_em $ mix do clean, compile
Compiling 2 files (.ex)
Generated plug_em app
plug_em $

is near instantaneous …