Alternative names to defmodulep (split thread)

private-modules

#1

What about defprivate?


Proposal: Private modules (general discussion)
#2

The problem with that names are p and private parts, because it’s not working as same as defp (again p - private - here). The point here is to have different naming like internal and optionally short it to i instead of private/p. So defmodulep as well as defprivate causes too many confusion. To be honest defprivate is even worse, because it does not refers to module in name.

defmodule Example do
  defprivate Sample do
    # …
  end

  defp sample do
    # …
  end
end

#3

Do you mean that internal used this way will avoid the confusion?

defmodule Example do
  internal Sample do
    # …
  end

  defp sample do
    # …
  end
end

Why not go for private then, it’s used practically in any language, even Ruby refers to private to declare a private something (a Module in this case) and uses the keyword private to declare private methods

module A
  module B
    def self.a; puts("A"); end
  end

  private_constant :B
  def self.a
    B.a
  end  
end

2.3.1 :074 > A::B.a
NameError: private constant A::B referenced

2.3.1 :075 > A.a
A
 => nil

My initial idea was defprivatemodule, but it’s too long and def_private_module it’s not in style.

Maybe it’s a bias I have, but I prefer the emphasis on the private part rather than on the module one.


#4

I said about internal/i and keep module part which you didn’t.

So here is real example:

defmodule Example do
  defmodulei Sample do
    # …
  end
  
  defp sample do
    # …
  end
end

Because private function (defp) is working differently than private module in this proposal (defmodulep). Look that private functions can’t be accessed in other modules at all, but there would be a way to access private modules even in other app (look my question about possible hack).


#5

I’m sorry, I’m confused.
Do you mean defmodulei?

Of course private functions work differently than private modules, they serve a different purpose.
The proposal was about private modules, but they are more like friend in C++.

I could have private modules with private functions…
In fact I probably will.


#6

Yup as you talking about private/p -> defprivate/defmodulep I’m talking about internal/i (just for example) -> defmoduleinternal/defmodulei.

Now look that both defprivate / defmodulep refers to private word as same as defp, but as you see they work differently, so there can’t be private/p word in proposed name. You know other languages, me too, but it does not mean that every newbie would know other languages as well. private which means two different things could be confusing for beginners.


#7

Not really a big difference…
private or internal are equivalent to my eyes.

I just see that defmodulei or defmoduleinternal are longer then defmodule and know it’s not a regular module.
In this regard, a p at the end helps more than an i.

They are both private.
At a different granularity, but still private (AKA not accessible outside of their scope).
A private function is not accessible outside of the module that declares it, a private module is not accessible outside of <still debating>.

It really means the same thing.


#8

It’s not a point to have big difference. The point is to have different naming even if those words are synonyms.

Again and again, helps for you, me and more experienced Elixir developers. Beginner would be confused why private/p does not work as same in both cases. Look that not everyone needs to read this proposition and not everybody even need to understand it.

No, they are not private as same as functions are. There are ways to access it outside unlike private functions. Depends on implementation there are few ways to access it.

Look:

defmodule Example do
   def private, do: 5
end
defmodule Example2 do
   def private, do: "Sample"
end

private means private, but 2 different implementations are 2 different implementations - not the same thing.

Imagine that I would write my own phoenix library. It would be also server, but it would work differently. Naming it phoenix is a big mistake as other developers seeing it could think that it’s fork or something like that. Looking at source they would find that there are 100% different even if both libraries do similar thing. Having same naming for different things is a big mistake as it’s confusing for others. While one team could assume that when we are saying phoenix we mean something else other people would see it still as original phoenix.


#9

Private means

belonging to or for the use of one particular person or group of people only.

That’s exactly what private functions and modules are.
But that’s not really relevant to the discussion, it’s just a minor implementation detail.

I guess we can agree to disagree on this.


#10

Again:

defmodule Example do
  @doc "This belongs only to use of one particular person or group of people."
  def private, do: 5
end

defmodule Example2 do
  @doc "This belongs only to use of one particular person or group of people."
   def private, do: "Sample"
end

defmodule PrivateTest do
  use ExUnit.Case do

  test "example" do
    assert is_integer(Example.private())
    # 2nd assertion would fail
    assert is_integer(Example2.private())
  end
end

# now look:
defmodule Problem do
  def confuse(module), do: apply(module, :private, [])
end
Problem.confuse(Example) # Integer
Problem.confuse(Example2) # String

Person A sign agreement for some job offer. Person B does not know if person A receives payment for hours, specific issue or maybe for whole specific project. That’s why person A should say is saying about agreement name instead of saying saying just about agreement. In any case he/she is working, but this does not mean all cases are exactly same. We could guess, we could assume, but nothing good would happen if we would do so.

That’s why people are using names. If you name something properly other people would understand you without taking care of “source”. Well we could name every pet just pet, because basically all pets are pets. Now you can’t filter images only for cats, because there are no cats - there are only pets. We could generalize everything and saying “thing” on everything, but that would be just confusing. :smiley:

Just take a look at:

Now say again that private in both cases means exactly same.

This topic does not talk about making some modules private. We are talking about guarding some modules to … (show warning, don’t allow access it unless). This is definitely not how private functions are working.

You think that private is private and there is no way to access private modules. In my linked example I show how it’s possible in specific implementation case. That’s why I mean it’s confusing. They are named similarly, but works differently. private means private, but here we have 2 different contexts which makes private word more than one meaning and that could be confusing.


#11

I really don’t want to monopolize the discussion.

Most of all, I’m not supporting any solution in particular, once we established private modules are going to be a thing, I’ll gladly accept any syntax for it, Jose and the Elixir team have already proven capable of handling those kind of changes with care.

defmdulei, defmodulep, definternalmodule they’re all fine to me.

But one thing is for certain: private modules are private modules.
They are something hidden from something else, based on some rule.

That’s not gonna change because rules are different for different elements.

Attributes are private too: they are only visible from inside the module and at compile time.
They just don’t have private in the name.

Private functions are private as well (hence the name).
Think of them as something like

defp private_func, visible_to: [MyApp.A] do
  ...
end

where visible_to can be omitted and defaults to [__MODULE__].

Private modules are private in the same way, but from an higher level, since we are dealing with cross module visibility and given that modules have only been public until now.
The fact that privateness is not enforced doesn’t make them any less private.

A simple analogy is private offices: nobody is locking himself up inside them, anybody could open the door and see what you’re doing, but they’re private nonetheless, and people usually knock, even though they are not forced to.

No, I don’t.

For now…
I think it’s just a matter of smoothing the transition to a full private module implementation.
Besides, if we are going down that road and implementing them, I would prefer the real deal.
I would also have preferred private to be an attribute of the module, even though I’m not a big fan of decorators, this one made sense to me and I think it’s more flexible.

I also think using internal would cause major headaches when they are going to become private for real (I bet it’s not gonna be that far in the future).
What’s going to happen to internal?
will it be deprecated? or will it be repurposed?

In the end private is just more future proof.
And the semantic is preserved.

The risk of confusion is exaggerated in my opinion: novices will read private and think of something they should not rely on, just like private functions, and that’ll be a good guess, even if they could work around the limitation, while experienced programmers are suppose to know what they are doing.


#12

Yeah and that was a bit confusing for me at start. I though how could I fetch them from other module. Btw. you probably did not noticed, but there is option to do it. :077:

yup, that’s 100% right
Now try to do that as a newbie and post a question on forum asking why it’s not working … It’s exactly why there should not follow same naming.

Or maybe other example. Write a library with def and defp macros. Think now some library is using it and somebody want to contribute to it. Unfortunately your implementation does not support guards, so commits which looks good fails which confuses everyone. In hundreds of lines file you need to find import or use which confuses everyone. Good if you are adding all declarations at top of file and you don’t have much of them …

Sorry, but I did not get what you mean by this. Is naked, shaved and young man still Santa Claus? :smiley: It’s like lake without water is still lake. Of course it’s normal for Python developer:

https://elixirforum.com/t/programming-related-jokes-thread/10652/21?u=eiji

but definitely not for Erlang developer. :smiley:

Look that Erlang private functions are definitely not like Python private methods. Same difference is in defp vs defmodulep. They have even different use case. First makes specific function private for specific module and second makes module magically hidden (for example scoped using some prefix). Hidden does not means private. Look that if there could be public database which is hidden and accessible only on specific IP address which is not assigned to any domain. It’s hard to find it, but it does not mean you can’t reach it. If you can reach it then it’s not private (again following what Erlang offers as “private”).

defmodule Example do
  defmacro defmodulep(name, opts, do: block) do
    quote do
      defmodule Module.concat(Private, unquote(name)) do
        @visible_to unquote(opts[:visible_to])

        unquote(block)
      end
    end
  end

  # requirep definition goes here
end

Such code would simply add Private. prefix before module, so you can’t access this module directly, but this just not mean that you can’t access it using Private. prefix. Private modules therefore are not protected (like private functions). They are basically still Public, but hidden under prefix/scope.

Look if I would say that last day I had guest then what would you think? You would probably think about human in my flat/house, but I actually I could mean bot on website. Both human and bot can be guests, they could have actually same privileges and they even could want same thing, but this does not mean that they are exactly same. Basically bot and human are 100% different - they could use same tools, one could imitate other, but if we would say about them with same words then other people could misunderstand what we are talking about.

We could even group all pets under one “pet” word, because all are pets. Now you need to guess which pet I’m talking about, because they don’t have their own names like cat or dog. If you firstly thing about dog and you would hear from me that I let it go on his way (like cat does) then you would be confused why I did it. You would call somebody to catch it and they therefore would assume that you are talking about dog. Well … you basically talking about dog, but you actually you are talking about cat even if you did not noticed it. That’s confusion and mess.

From my experience more you assume is more problems in future. Having same naming (private) for 2 different similar, but different things (hiding and protecting) would not give anything good. Every developer was newbie, so he/she should remember how much things he/she wrongly assumed. While = operator is confusing it’s important for Elixir itself. If we can we should not confuse newbie and don’t let assume too many things.

Coming from this point people (much more experienced from me) defined rules which makes everyone’s code easier to understand like proper indention, max line length, max function length and also proper naming. I believe most of things could be generalized to x, y and z variables, but this does not mean that it should be done in such way.

And that makes them not private. There is even no way to protect use “private” modules in other apps. Still I could wrote defmodule Ecto.MyHack do … end and I would be able to access all internal modules which are scoped for Ecto.* modules only.

Again all what I mean is assuming what typical newbie do. Let’s say you have really important core function which you don’t want to be accessed. When you hear about “private” modules then you most probably assuming that they are working as same as private functions. If we would use synonym then newbie think: Hey … why it's named differently? Does it not work like this one …?. This makes newbie curious about it and answer for his question would be available in documentation. If we would have same naming and newbie would assume that it’s working as same then he would not even look at documentation and would not notice that’s not working in same way. Wrong assumption could make code vulnerable and it could be pretty easy used with simple hacks I mentioned few times.

Imagine that someone would write library and instead of using private functions all write calls would be in private modules. This would allow to access them and write data for example without protection (like function guards) which is … not needed for “private” modules, because they are like private function, right? If I can imagine something like this in “5 min” then what could do expert taking his time with coffee cup?

ERROR: WRONG ASSUMPTION FOUND !!!

  • Declaring the module visibility per package or application. The Elixir language and the compiler do not have the concept of “applications”. Applications and packages are purely a build tool construct. In a way this is great, because the language is small and we build features on top, but it also means we cannot implement a construct such as visibility per package as part of the language.

Also you really did not get why this proposal was created. It’s not about protecting specific module - it’s more about don’t give so easy access without any warning/error (depends on scenario). Access is still possible no matter if by hacks or not. It’s not going to be protected in future. This proposal does not mean that core team want to introduce private modules which in future would not allow to be accessed. This is all about don’t ignore reaching internal API.

Even if core team would change mind then they can’t change behavior of already implemented functionality until next major release i.e. 2.0.0. I don’t believe that 2.0.0 would be planned soon as there are not enough changes for such major version. Look that 2.0.0 means breaking backward compatibility, so core team would probably not dot it as often as you imagine, because there would be lots of problems with already created packages. Think that all authors of all packages would need to check if there are not any breaking changes in their code. This is lots of work and it would be definitely announced much before it’s release.

ok, I’m totally messed now …

Did you read proposal and why it was proposed? It was exactly because people tried to access things which they should not and it was not talked in contents of not used libraries, but lots of question/issues about “… not working after upgrade”. If there would be a way to access private functions I believe that lots of people would do so and I’m not exception here. I really often want to change one line in function which relies on few private functions, but copying them means maintaining all of this just for one line change and all fixes to this code would need to be tracked by me and patched in my version as well.

I’m curious what you mean by “experienced” developers, because if I remember correctly those awesome people which wrote absinthe had problems with upgrading Elixir too. Here are only few examples which are already mentioned:

and the complete list is really long.

Look I don’t believe that José Valim would create such proposal for few newbies … There was lots of problems which I have noticed too using popular 3rd-party libraries.


Maybe after all of that you would not understand - no problem. I would like only to write why it’s so important for me. As a newbie I saw monkey patching in Ruby used only once for one line function. I liked it way too much. I end up with monkey patching lots of functions, because I just saw other people do that. Of course it was not only one thing I did wrong and mostly it’s all because I assumed too much. It’s why taking care about whole code (not only naming) is so much important for me.

Hope that helped :slight_smile:


#13

I think the bikeshed should be painted green, for what its worth :wink:


#14

I haven’t read through all the threads on this topic but has the community thought about going the route Rust took, where every function is private by default? To make something public you have to declare it public like:

pub def this_is_my_public_facing_api(), do: # do something great

This way we don’t have to worry about private modules or whatever.


#15

Since erlang requires a declaration of which functions are exported, yeah, I think the idea was considered. I have mixed feeling on the result, but I don’t question its consideration. In any case this is a majorly backwards incompatible change that would require a very strong argument to support it.