Stream: ideas

Topic: `roc organise`


view this post on Zulip Kasper Møller Andersen (Dec 08 2024 at 23:13):

As an off shoot of #ideas > Formatting the ordering of tags in source code, I want to just talk about navigating code in general. And although I have some concrete ideas in here, it's really just for the fun of it, and I just want to explore the space a bit. So it's probably going to be a bit brain-dumpy, but I hope it will be interesting none the less!

Something that continues to come up for me in writing code is: actually navigating that code. It kind of relates to readability, but it's also kind of its own thing. It's basically about having a sense of place in both a given project, but also just within a file. Having a sense of place makes it easier to navigate in general, because you know where you are and how to get elsewhere.

In many pieces of code, that's not a widlly important property though, because we often just jump around using various tools ("go to definition" and "show usages" being the big ones).

But it's also something that comes up at other times for me. For example, when I need to create a new function, where do I actually put it? Does this file have sections? Maybe there's a section this function belongs in? Now my new function is going to call another function I will add afterwards. Where do put that? Just after the previous one? That's what I usually do.

There's a lot of happenstance organisation happening there, and if come by some file where I'm not really sure what I'm looking for, trying to make sense of how things are organised in it is usually quite painful. I can't tell which sections are in the file up front (if there any at all). In this case, I could really use a map of some kind, or some rough idea of how things are laid out.

And that's actually what #ideas > Formatting the ordering of tags in source code is about: having a map. Coming from Elm, it's common to have large Msg types, which have tonnes of tags. All of these are matched in an update function, and navigating between different Msg tags easily gets messy. So what if the type definition of a Msg type could effectively also be the map for that type?

An important difference between a tag in such a type and a function is that while I can easily find a function by going to its definition, I often don't care about the definition of a type, but rather how it's used. And that means good navigational structure starts to get more important, because I have to figure out how different uses of the type relate to each other.

My personal preference for navigation in the real world is to rely on various "landmarks". Not necessarily obvious landmarks, like tourist attractions, but also just my understanding of the land. For example, if I need to find some coffee shop, the best way for me to find it would be to have instructions on how to reach it from some other nearby shop I already know the location of ("Oh yeah, Java is just across the street from Scala, you can't miss it!")

In a code project, we tend to organise by folder names/namespaces, and this works pretty well I think. It plays nicely with file systems, which means you already have a rough idea of how to navigate around any project that is built on files. In navigational terms, that's sort of like cities or neighbourhoods.

But once inside a file, there are few rules left you can rely on. Import statements are probably going to be at the top at least. In Roc, you can also tell the exports at the top. But after that, all bets are off. If a file is a house, it's like knowing where the front door is, and not much more. You can jump around the house by "going to definition" and all that, but before you know it, your tools have pulled you into a completely different house (well, file) and you may not even have noticed. In that case, I usually need to stop and just figure out where I am, because it's important to understand this in order to figure out what my options are. What module am I inside? Which functions are exposed to it? And so on. All that changes when I switch file. Having good landmarks and maps makes it easier to figure out where I am and what I can do.

Which brings me to an experience from an old project I worked some years ago: using tslint and its member-ordering rule.

You could actually lay out rules that help you navigate inside a file! I don't remember the exact configuration we used (mainly just public stuff before private I think), but it was quite pleasant. It made both navigation and figuring where to put things when you add them easier, for the same reason zero-config formatting makes things easier: regardless of whether the rule is good or bad in itself, it is consistent, and that's the best.

Anyway, that's a lot of rambling on my part, so I want to throw some ideas out there:

Hope you found my train of thought entertaining if nothing else at least :smiley:

view this post on Zulip Eli Dowling (Dec 09 2024 at 01:43):

I agree with some of this, and I like the idea. I actually think one of the things OOP gets right is organisation. Classes provide nice boundaries and a plan for how state methods etc should be layed out.

My favourite FP organisational trick is ordering by dependency.
Ocaml and fsharp both have this property, and I love it. Files are compiled in order, and. You can only access things after they have been compiled, both within files, but also between files. It's excellent.

It means basically every project looks like this:

Types
Utilities
Reusable modules
Project specific modules
Main

And vscode lets you browse your files in "compilation order"
Every fuel then follows the same pattern:
Types
Utilities
Reusable functions
Problem specific functions
Output.

When people first arrive, myself included, they pretty much always say "wtf is this shit, why can't I put everything wherever I like??"

After using it for a month I very rarely had any errors and now never have them.
But reading fsharp and ocaml code is way easier because of this. You start at the top and go down. You know for a fact that the first things have no dependencies within the codebase and they everything branches in untill you solve the problem this module is solving.

Reading people's code in AOC I see some people have a similar strategy to that, and others don't. I immediately find it easier to Grok code using that organisational method.

view this post on Zulip Brendan Hansknecht (Dec 09 2024 at 03:01):

My favourite FP organisational trick is ordering by dependency.

I actually really dislike ordering by dependency. It is required by c/c++ and I think it is the wrong order. It puts all the noise and unimportant functions at the top of the file.

I much prefer ordering by reverse dependency (still often requires subgrouping to some extent). If I see a function do_foo that calls do_bar and do_baz. I much prefer to see do_foo at the top followed by do_bar and do_baz. This enables me to start with the high level algorithms and most important entry points. I then can scroll down as far as I want to continue reading the next biggest chunks. If at some point I think I get the idea well enough, I can stop scrolling. No need to get to the bottom of the file with all of the innermost details.

This is not perfect in modules that expose tons of functions, but the rough idea of it is really useful in my experience.

view this post on Zulip Kilian Vounckx (Dec 09 2024 at 05:42):

I also prefer the main function at the top. This ordering also means public functions will automatically be above any helper function they depend on which I prefer.

I do like most of my types at the top though

view this post on Zulip Brendan Hansknecht (Dec 09 2024 at 06:16):

Yep

view this post on Zulip Kasper Møller Andersen (Dec 09 2024 at 07:12):

I definitely also prefer forward references when reading. I do appreciate that it gives structure to the project and files, but it feels backwards none the less.

view this post on Zulip Kasper Møller Andersen (Dec 09 2024 at 07:16):

Is there a plan to have the formatter reorganized exports based on doc comments similar to Elm? I’m wondering if applying that same organization to the code itself would be a useful trick.


Last updated: Jun 16 2026 at 16:19 UTC