You can do that without a plugin:
@tomjaguarpaw sure I can flesh out the proposal more later. But to answer your question, by “colocated”, I mean in the same directory, e.g.
-- current
Foo.hs
Foo/Bar.hs
-- proposal, would act the same as current
Foo/Mod.hs
Foo/Bar.hs
Yes, the GitHub comment is certainly not enough to drive the whole change, it’s just a bonus to the overall proposal. I had assumed people have experience with Rust or Typescript, which use this idiom extensively, but perhaps I shouldn’t assume that. Here’s a more concrete reason:
I like to have my tests match the same module structure as my source code. If I want to test a module and a submodule, I have to do
src/Foo.hs
src/Foo/Bar.hs
test/FooTest.hs
test/Foo/BarTest.hs
It’s a little awkward that Foo/BarTest.hs is testing a submodule under the module FooTest.hs is testing.
The proposal would allow
src/Foo/Mod.hs
src/Foo/Bar.hs
test/Foo/ModTest.hs
test/Foo/BarTest.hs
Which makes it much clearer that ModTest.hs and BarTest.hs are testing behavior in the same area. The module names will be a bit different (Foo/Mod.hs => Foo, Foo/ModTest.hs => Foo.ModTest), but I’m willing to live with that.
@Ambrose that’s a bit different from what I’m proposing. I don’t want Mod.hs to reexport everything in all submodules. The same way you might implement some stuff in Foo.Bar and break out some helpers into Foo.Bar.Baz, I want the same thing, except putting the file containing Foo.Bar in the same directory as Foo.Bar.Baz.
@taylorfausak yes, that preprocessor does what @Ambrose suggested, but not what I’m asking for. I want to change the compiler to automatically find Foo/Bar/Mod.hs for an import Foo.Bar when Foo/Bar.hs doesnt exist.
Is it? Why?
🤷 Call it personal preference.
This concept is just pretty widespread in other languages, and I miss it every so often in Haskell, and was wondering if anyone else feels similarly. Seems like probably not
For what it’s worth, I do. I doubt the Haskell community will want to flex on this for such a minor benefit as ‘convenience of people clicking around unfamiliar projects’, but I am also inconvenienced when I’m rifling through SomeProject/Frobber/
searching for the reason something is being frobbed in an unexpected way, only to find that I needed to jump up a level to read SomeProject/Frobber.hs
.
…like the old WWW custom of having an index.html
file in directories so that:
https://www.example.org/look/here/
is expanded to
https://www.example.org/look/here/index.html
…?
Agreed - it’s their problem, so let them solve it (or provide a patch). We already have enough “conveniences” to quarrel over e.g:
> chdir that/path/to/SomeProject/Frobber
> view ../Frobber.hs
…?
Correct. As I said, an inconvenience, not a blocker.
This simply doesn’t match how directories are used most of the time, outside of module systems that resemble Haskell’s. If I told you I have a taxes
directory, with files taxes/2020.pdf
, taxes/2021.pdf
, taxes/2022.pdf
, etc., where would you expect to find a text file that contains data spanning multiple years? In taxes/all-years.txt
? Or taxes.txt
alongside the taxes
directory? If I told you that system service vorpal
writes error logs to /var/log/vorpal/error.log
, where would you be more surprised to see non-error, normal operation logs? /var/log/vorpal/info.log
, or /var/log/vorpal.log
?
Tools like ripgrep will automatically search for their needles recursively in the directory you are in, or in the directory you specify. They don’t reach out to the parent directory, look for a file whose base name matches your current directory, and search there too. Why would they?
So if I’m in SomeProject/Frobber
, of course I can manually run rg "some text" . ../Frobber.hs
, if I remember to. I can also spend five minutes writing a shell function that I train myself to use in place of rg
that does this for me. I can do any number of things to work around this minor inconvenience. But the OP thought he might be alone in experiencing it, and he isn’t.
Questions:
-
if
AnotherProject/Blagger/Mod.hs
, already exists, what happens then? -
If
Mod.hs
is the only file inSomeOtherProject/Grebber
, should:SomeOtherProject/Grebber/Mod.hs
be considered “the same as” :
SomeOtherProject/Grebber.hs
…?
My idea was to only fallback to Foo/Bar/Mod.hs if Foo/Bar.hs does not exist. And one could still explicitly import Foo.Bar.Mod
if they wish
And yes, Mod.hs works independent of any other file. Literally the only change would be changing the behavior from
let f = replace "." "/" importMod <> ".hs"
to
let f' = replace "." "/" importMod
f <- bool (f' <> "/Mod.hs") (f' <> ".hs") <$> doesFileExist (f' <> ".hs")
Making a toolchain chain to appease a MS-funded tool is totally silly. Talk about putting the horse in the cart and giving it the reins lmao.
…and no doubt it only took a few patches/s for do
notation to “go viral” too. Moreover, this to me looks far too similar to how Subversion worked back in “its heyday” - one .svn
sub-directory for each directory of a codebase it managed. So it’s bemusing to be told that other presumably-newer languages are now (to an extent) following Subversion in that way.
I don’t personally see the benefit of this specific change, but I do like the idea of being able to separate the concern of “where the file is on a filesystem” from the concern of “what Haskell module does this file contain”. I suspect that belongs in a build too though, not GHC. (Maybe some tool already has it. I don’t know.)
Where have I encountered this idea before…that’s right, it was here:
Sigh … I also still (still!) get tripped up by logically related modules getting forced into different directories, and would probably use something like Mod.hs if it existed.
That said, there is a certain simple usefulness in modules corresponding 1-1 with the directory tree, and the proposal is a change that’s too disruptive to the ecosystem to have legs.
On the upside, no tooling support is required if you simply stop using Foo directly and only use Foo.Mod I might try something like that some time…
Sigh … I also still (still!) get tripped up by logically related modules getting forced into different directories, and would probably use something like
Mod.hs
if it existed.
If they’re that “logically related”, could you just move such modules to their own (sub)directory?
[…] no tooling support is required if you simply stop using
Foo
directly and only useFoo.Mod
[…]
…but will you be happy with staring at the ".Mod"
suffix on every third, fourth or fifth import
everywhere? For a while, Rust had a reserved word (but for a different purpose) and it was eventually removed due to overuse, to the point of being irrelevant:
https://mail.mozilla.org/pipermail/rust-dev/2013-April/003926.html
Alternatively, how many here thought -XBlockArgument
“seemed reasonable at the time” ?
I’m confused. Your first two paragraphs seem to contradict each other, or at least lead directly to the solution which you’ve been arguing against. That is, tooling support for having logically related modules in the same directory, without requiring one of them to have a suffix in the module name.
Maybe that Mozilla link would make things clearer, but it’s broken.
Also, I continue to think BlockArguments
is reasonable, and don’t seriously expect anyone to abuse it as in the thread you linked. So I’d say any parallels are a stretch.
Like @chreekat, I’m sympathetic to this idea, and would probably use it if it became standard, but I’m not sure it’s worth the hassle.
I might be completely and utterly wrong here, but isn’t this achievable if we break the whole compilation unit = file = module
thing, specifically the file = module
part?
I imagine if you had the file Foo/Mod.hs
and then Mod.hs
looks like this
module Foo where
...
Would that achieve what OP is asking for?
Not to imply at all that it’s so easy to do or anything, just asking theoretically
…because they addressed two separate points:
-
The first one posed an alternative solution: a new directory to hold those “logically-related” modules, to avoid them “getting forced into different directories”.
-
The second one was speculative: if per-directory
Mod
modules were used everywhere (much like what some seem to be doing with-XBlockArgument
) would we be happy with that, or would constantly seeing all thoseMod
suffixes be irritating?
As for the now-broken URL:
https://web.archive.org/web/20140722002037/https://mail.mozilla.org/pipermail/rust-dev/2013-April/003926.html
…look for the response starting with:
“It shouldn’t be too complex” is sadly optimistic […]
GHC also supports naming your file Foo.Bar.Mu.hs
and skipping the directory structure, which I prefer, but a large amount of tooling assumes the Foo/Bar/Mu.hs
convention. So in practice if you want people to enjoy collaborating with you there isn’t a choice.