other-languages

2022-08-01T05:57:32.544889Z

One feature of Elixir I can't tell is madness or genius or both, but it's the backbone of the Phoenix framework and why they managed to make a framework feel similar to Rails in my opinion, is the use keyword and using macro. You can read a bit about it here: https://brooklinmyers.medium.com/using-use-usefully-in-elixir-and-phoenix-b59a5ea08ad2 I think you can do the same in Clojure, without any changes to the language either, it's kind of just a convention, though you'd need to require some things so it be a little less magic. Basically, it's like calling a macro from another namespace at the top-level of your namespace which will return a bunch of top level code injected into your own namespace. That code could itself require or import more things into your namespace or call more of those injecting macros from other namespaces. Think something like:

(ns foo)
(defmacro using []
  `(do (require [clojure.string :refer :all])
       (defn render [name]
         (join "," ["Hello" name "welcome!"]))))
 
(ns bar)
(foo/using)

2022-08-01T06:07:55.116209Z

It kind of end up being used similar to inheritance in OOP, so one Elixir module inherits from another, but it's done literally by just capturing the code from another module into your own through the use of a macro. It's hard to know if that means it has all the same problem inheritance has or not... But it's magic in that it can turn a module into a convention with a lot of default functionality loaded in and some extension points. And it can also automatically require and refer a bunch of stuff.

2022-08-01T06:10:09.305609Z

Compare:

defmodule TodoWeb.ItemsController do
  use TodoWeb, :controller

  alias Todo.Items

  def index(conn, _params) do
    items = Items.list_items()
    render(conn, "index.html", items: items)
  end
end
with:
package TodoWeb;

import Todo.Items;

class ItemsController extends TodoWebController

  public index(conn, params) {
    var items = Items.listItems();
    this.render(conn, "index.html", items);
  }
}

mauricio.szabo 2022-08-14T19:24:55.486349Z

I honestly hate the use macro

mauricio.szabo 2022-08-14T19:25:39.218479Z

I had SO MANY libraries I could not use outside Phoenix, or ExUnit, or things like that because these libraries expected me to use SomethingFromPhoenix or whatever

mauricio.szabo 2022-08-14T19:26:38.596739Z

Sure, I could track which functions I was not implementing and provide mocked versions for it, but again, this all could be avoided if use didn't exist at all, so library authors would not be able to "cut corners" like that...

2022-08-15T06:19:07.749759Z

That's an interesting data point, I hadn't thought of like other libraries, though I don't know, is that the fault of use or of those pretending to be libraries's libraries?

Martynas Maciulevičius 2022-08-03T08:21:44.951089Z

Why not create a module interface and use Java inheritance? Also this could then allow to use some kind of dependency injection or parameter-based extension and not inheritance

2022-08-03T16:50:14.566229Z

What do you mean module interface? In Elixir they don't have inheritance, similar to Clojure. So in order for a namespace to automatically have some functions and variables "inherited" from a base, it uses a macro to copy them over. The result is not just that you can use those from inside the namespace, but that other namespaces can use them from your namespace.

Martynas Maciulevičius 2022-08-03T18:45:37.874369Z

I meant to use Java's interface. But I see that you want to use a macro. Which is fine. I haven't used Ruby but I think it's OOP. So why not reuse Java with Clojure's defrecord and reify. 🤔

2022-08-03T20:36:16.130179Z

It's not Ruby, it's Elixir, they share a similar syntax. Interfaces and therefore defrecord/defprotocol doesn't get you the same thing as inheritance. Which is why people in Elixir came up with this strange macro instead. The macro can pull in vars, requires, imports as well as functions.