Organizing your code files

Hi List, I'm a relatively new Haskeller. I've gotten the syntax of the language more or less down, and I'm slowly expanding my vocabulary. I've found in other languages that my coding style and how my code is organized affect both the quality of my code and how easy it is to understand. 'Coding style' refers to how code is organized within a block. For example: ``` foo bar baz bing = x + y * z ``` versus: ``` foo bar baz bing = x + y * z ``` Code organization refers both to how code is organized within a source file (i.e., how code blocks are arranged, how they're grouped, so on) and how source files themselves are organized. I've got the first part down--I've developed a code style that I'm satisfied with, I've tried to fine-tune my style to make maximum effective use of available space (limiting myself to 72 columns, and a block size of no more than 30 lines) so as to force myself to extract and abtract away unnecessary details. The second part is the one I'm having trouble with. There are so many ways to organize one's code that I don't even know where to begin developing a method that I like other than to ask other Haskellers how they do it and why they like doing it that way. So there you have it: How do you organize your code, and why? Cheers! -- Christopher Conforti

Hi Christopher! On my end, I make use of automated style formatters, so that this kind of thing can be kept consistent during CI. I invite you not to think too much about the micro-details of code styles, but rather find a tool that you can configure so that your style can be enforced automatically. You'll be able to spend more cycles on programme development. I can give you an example of a recent library I wrote: https://github.com/Kleidukos/effectful-contrib/blob/8ae8ad1d76e0c43610619fc0... Note that: * Type variables are made explicit, and as such I annotate even the simplest of them (with `:: Type`) * I align the separators (., => and ->) * I use stylish-haskell & hlint * If you want finer automated control, try something like Britanny. Le 07/08/2021 à 21:18, Christopher Conforti a écrit :
Hi List, I'm a relatively new Haskeller. I've gotten the syntax of the language more or less down, and I'm slowly expanding my vocabulary. I've found in other languages that my coding style and how my code is organized affect both the quality of my code and how easy it is to understand.
'Coding style' refers to how code is organized within a block. For example: ``` foo bar baz bing = x + y * z ``` versus: ``` foo bar baz bing = x + y * z ```
Code organization refers both to how code is organized within a source file (i.e., how code blocks are arranged, how they're grouped, so on) and how source files themselves are organized.
I've got the first part down--I've developed a code style that I'm satisfied with, I've tried to fine-tune my style to make maximum effective use of available space (limiting myself to 72 columns, and a block size of no more than 30 lines) so as to force myself to extract and abtract away unnecessary details.
The second part is the one I'm having trouble with. There are so many ways to organize one's code that I don't even know where to begin developing a method that I like other than to ask other Haskellers how they do it and why they like doing it that way.
So there you have it: How do you organize your code, and why?
Cheers!
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD

On Sun, 8 Aug 2021 08:50:24 +0200
Hécate
On my end, I make use of automated style formatters, so that this kind of thing can be kept consistent during CI. I invite you not to think too much about the micro-details of code styles, but rather find a tool that you can configure so that your style can be enforced automatically. You'll be able to spend more cycles on programme development.
Indeed, this is the direction I'm heading. However, I'm doing it by
hand first so I can refine my methods to a point I'm satisfied with,
and then build the automation afterwards, hopefully doing it right the
first time. I place value on this because I plan to stick with the same
style throughout all my code so that I can read and reuse my own code 30
years from now and not have to think about how much I hate past me.
The sort of information I'm after answers questions similar to and
including, "Why put this typeclass in this source file and not that
one?" and "Should I put all my type signatures, typeclasses and data
structures at the top? At the bottom? Should I put them in a separate
source file?"
Having outside perspectives from actual people that I'm having
conversations with will help me both learn the finer points of the
topic (from actual programmers) and piece together a strategy I'm happy
with.
On Sun, 8 Aug 2021 08:50:24 +0200
Hécate
Note that:
* Type variables are made explicit, and as such I annotate even the simplest of them (with `:: Type`) * I align the separators (., => and ->) * I use stylish-haskell & hlint * If you want finer automated control, try something like Britanny.
Your code is a pleasure to read; I shall enjoy studying it. :-) -- Christopher Conforti

I think I can provide answers to those questions, based on what I have seen in the ecosystem so far:
Why put this typeclass in this source file and not that one?
Should I put all my type signatures, typeclasses and data structures at the top? At the bottom? Should I put them in a separate
I can read and reuse my own code 30 years from now and not have to
As you may know, modules can export other modules they depend on, but I would advise to put the typeclass definition in its own .Class module, such as Control.Monad.IO.Class https://hackage.haskell.org/package/base-4.15.0.0/docs/Control-Monad-IO-Clas.... source file? Here is the layout I have seen most used: module ( export1 , export2 ) where imports data types instances type aliases newtypes instances functions (main API) functions (helpers) More generally at the project level, make sure you have a tree with few chokepoints. I encourage you to read this article https://www.parsonsmatt.org/2019/11/27/keeping_compilation_fast.html. Cheers. PS: think about how much I hate past me. This is bound to happen whether or not you have a strong & enforced formatting style. We simply grow up as programmers with time. Le 12/08/2021 à 00:27, Christopher Conforti a écrit :
On Sun, 8 Aug 2021 08:50:24 +0200 Hécate
wrote: On my end, I make use of automated style formatters, so that this kind of thing can be kept consistent during CI. I invite you not to think too much about the micro-details of code styles, but rather find a tool that you can configure so that your style can be enforced automatically. You'll be able to spend more cycles on programme development. Indeed, this is the direction I'm heading. However, I'm doing it by hand first so I can refine my methods to a point I'm satisfied with, and then build the automation afterwards, hopefully doing it right the first time. I place value on this because I plan to stick with the same style throughout all my code so that I can read and reuse my own code 30 years from now and not have to think about how much I hate past me.
The sort of information I'm after answers questions similar to and including, "Why put this typeclass in this source file and not that one?" and "Should I put all my type signatures, typeclasses and data structures at the top? At the bottom? Should I put them in a separate source file?"
Having outside perspectives from actual people that I'm having conversations with will help me both learn the finer points of the topic (from actual programmers) and piece together a strategy I'm happy with.
On Sun, 8 Aug 2021 08:50:24 +0200 Hécate
wrote: Note that:
* Type variables are made explicit, and as such I annotate even the simplest of them (with `:: Type`) * I align the separators (., => and ->) * I use stylish-haskell & hlint * If you want finer automated control, try something like Britanny. Your code is a pleasure to read; I shall enjoy studying it. :-)
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD

On Thu, 12 Aug 2021, Hécate wrote:
Here is the layout I have seen most used:
module ( export1 , export2 ) where
I prefer terminator style, because that simplifies re-ordering of lines: module ( export1, export2, ) where
More generally at the project level, make sure you have a tree with few chokepoints. I encourage you to read this article https://www.parsonsmatt.org/2019/11/27/keeping_compilation_fast.html.
I second to avoid a big Types module. I propose to define one module per major type, name the module after the type, such that you can make use of qualification, like Text.concat, Window.open, Path.join, Matrix.transpose. You may still need Private modules in order to break import cycles and share secret definitions between the modules.

Am Do., 12. Aug. 2021 um 00:33 Uhr schrieb Christopher Conforti < christopher@conforti.xyz>:
Indeed, this is the direction I'm heading. However, I'm doing it by hand first so I can refine my methods to a point I'm satisfied with, and then build the automation afterwards, hopefully doing it right the first time. [...]
IMHO this is exactly the wrong way round: Pick one of the existing formatters (hindent, brittany, ormolu, ...) and stick with it for a while. If you don't like what you see, you can try another one. But the main point is: These formatters embody good practice learned the hard way from lots of other people, and using them will make your code more readable not only by you, but by others, too. The "not invented here" syndrome is seldom a good idea, and consistency is far more important than small details you might not like. Cheers, S.

If you put calling functions before called ones, then when you reload GHCI
and it dumps a lot of errors at you, the last one is usually a good place
to start, because it depends on nothing else flagged as an error in the
same module.
Also I always flag mutually recursive functions or types with a
hard-to-miss "PITFALL: mutually recursive with <set of functions>" comment,
as those are easier to understand if you begin recognizing the mutual
recursion.
On Thu, Aug 12, 2021 at 2:11 AM Sven Panne
Am Do., 12. Aug. 2021 um 00:33 Uhr schrieb Christopher Conforti < christopher@conforti.xyz>:
Indeed, this is the direction I'm heading. However, I'm doing it by hand first so I can refine my methods to a point I'm satisfied with, and then build the automation afterwards, hopefully doing it right the first time. [...]
IMHO this is exactly the wrong way round: Pick one of the existing formatters (hindent, brittany, ormolu, ...) and stick with it for a while. If you don't like what you see, you can try another one. But the main point is: These formatters embody good practice learned the hard way from lots of other people, and using them will make your code more readable not only by you, but by others, too. The "not invented here" syndrome is seldom a good idea, and consistency is far more important than small details you might not like.
Cheers, S. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- Jeff Brown | Jeffrey Benjamin Brown LinkedIn https://www.linkedin.com/in/jeffreybenjaminbrown | Github https://github.com/jeffreybenjaminbrown | Twitter https://twitter.com/carelogic | Facebook https://www.facebook.com/mejeff.younotjeff | very old Website https://msu.edu/~brown202/
participants (5)
-
Christopher Conforti
-
Henning Thielemann
-
Hécate
-
Jeffrey Brown
-
Sven Panne