Aaron observed that the module
declaration contains exclusively redundant information. The module name is determined by the file name, and the exposed values are determined by the module comment. So why have both?!
Here is the proposed module header in this world:
Notice that I am suggesting syntax highlighting to emphasize the exposed values. I think this idea is much worse without that affordance.
-
No
module
declaration — Right now if you leave of themodule
declaration, it means that the module name isMain
and it exposes everything. If you leave off the module documentation, the module name would be the file name and it would expose everything. -
port module
— If there is nomodule
declaration, how can you have aport module
declaration? We could just start the file withport module
anyway. That would be the first line, then the documentation comment. -
effect module
— If there is nomodule
declaration, how can you have aeffect module
declaration? We could just start the file witheffect module { command = MyCmd }
anyway.
- When importing
X.elm
, the module name inmodule X exposing (..)
never gets out of sync with the file name. - If you move a file, you do not need to change the module name as well.
- You do not duplicate all the exposed values in
exposing
and in the module documentation.
- It is weird that comments have real meaning. The
exposing
info is important, so you cannot just throw this comment out! - The concept of a
module
is not really introduced. People will just see files. People already miss the importance of using modules to hidetype
information, and this could make that even worse.
We also ended up talking about exposing type
constructors.
Right now you can say module Maybe exposing (Maybe(Just))
, only exposing one constructor. So users have a constructor, but they can never pattern match on it. I cannot see where this is desirable.
So as we discussed this scenario, we decided that we currently have two reasonable scenarios: you have everything exposed or nothing exposed. So why not restrict it to those with Maybe(..)
and Maybe
?
Well, there are certain times when writing packages where this feels too limiting. In my parsing library:
- You have a
type Parser
that should be opaque to users. No constructors exposed. It lives in theParser
module. - You have the
Parser.LanguageKit
module that defines a bunch ofParser
values and needs access to the constructors.
The current solution is to create a Parser.Internal
module with a type Parser
that exposes all constructors. From there it can be used by all modules within the package. Then in the Parser
module, you can say
type alias Parser a = Parser.Internal.Parser a
So people from outside can see the type but not the constructors. This does not feel amazing, but it works.
So we were thinking, what if there is actually a third scenario? A type
can be (1) no constructors exposed, (2) all constructors exposed to everyone, or (3) all constructors exposed within this package. Now you can structure your package code however you want and still hide the information from users.
Proposal: We could have three ways to expose a type
in this new world: Maybe
for hidden, Maybe(..)
for exposed to everyone, and Maybe(..local..)
for exposed within the package. I am not satisfied with the syntax for the third case, and would be curious to find something better. In the end, it will only be useful in relatively sophisticated packages (never applications) so it is alright if it is a bit odd.
Note: I think inability to do (3) is the root cause of this proposal. Having reexports is a huge problem for creating reasonable documentation. If I just expose a whole module, do I just put a link to that module? That is what Haskell does and it makes docs like these incredibly difficult to read and understand. So maybe we take the module doc for the rexport and just jam it in there?
My point here is just that I think (3) solves the underlying problem that gives rise to this request in all the cases I can think of. It would be worthwhile to think of cases where this is not true.
Avoiding
module
declaration is very good idea - it will save a lot of time (manual work) during module refactoring (replacing the file)! Vote for this! 👍My thoughts about it:
No code in comments & No comments in code
Maybe this syntax would be nicer?
In this case we have simpler doc comment syntax:
--| ..
instead of{-| ... -}
Simpler export syntax
Instead of strange
@
+exposing
keyword it would be nicer to have justexport
. It also looks good as symmetrical word ofimport
:Simpler import syntax
We also could simplify
import
expression avoidingexposing
keyword:Using "import .. as"
Now it has even better symmetry with
export
expression!Type constructor access level
It is similar to "access-modifers" in Java:
public
,private
,package private
.So we could have syntax like this:
Export type
Export type just by name. No need for
T(Constructor1, Constructor2)
orT(..)
syntaxDefine type
Or shorter: