Code splitting (Modules) and common data usage questions

Hello everyone,

I’m Kristian, an Italian programmer, and I’m both new here in the community and new regarding the Elm language, but it feels just right to join the Elm community as you guys seem pretty kind to everyone and very passionate about Elm.

I have started learning Elm because it seems fast, clean and, in some ways, restrict you to one, at least general, way of doing/structuring things, bot via the language itself both with the guidelines written in the elm official guide. Also, its state about no runtime errors, provided by the core language constructs, is appealing.

I have now started a simple project, which you can discover publicly here on Github, just to get some better understanding of Elm by actually using it, about a single-page website which allows defining via UI n variables (each with some details, like, for example, name, value and colour) then selecting one or more from n predefined graph styles and then it will show the above variables plotted, or in a bar chart, etc. using the SVG library from the elm/svg package.

I’ve started with a single “Main” Elm file which describes the Html used within the page, like the inputs and links them to update the model so that the page knows the variables and the selected graphs. Then, based on this information, I would like to effectively draw some SVG graphs (I’m not using any graph-ready package on purpose, don’t even know if they exist at the moment).

Following the official guidelines, but obviously, I could have made and will make some mistakes on the road, I started by keeping the whole system simple and easy to read, then started to add complexity, also by making some refactoring, for which the compiler has been very useful.

I’m now at the very beginning of this project and, as soon as I started to write down functions to actually draw SVGs, the main Elm file started growing and I have started wondering if I could split the code so that (it sounds like a nice solution in my head) the main Elm file just handles the UI of the page and the state of variables and selected graphs, then it would correctly call some function from other modules for each graph (or type of graph). For example, if I want a plotter, I’m going to add a Plotter.elm module which knows how to draw a plot of some data, then the main Elm file would call it if I select the “plot” type graph on the website page. Then, if I would like bar charts, I would create a BarChart.elm file and so on (more or less).

The questions I’m facing right now are 3 ones.

1° question
Would, this solution, be correct and adheres to Elm official guidelines?

2° question
Since I have the variables (my data) typed in the main Elm file, how can I give the other modules the ability to “see” that data type if I also have to import the modules into my main Elm file? (read the article about circular dependency and how to avoid it, but I can only see the solution of creating a “Data.elm” file which I can import in all modules I need to, both drawing and main modules, solution n°2 in the article)

3° question
Since I would surely like to add some customization settings to the graphs (for example, in a bar chart I would like to see one variable above another or maybe I would like to change a variable colour only for a specific graph, etc.) where these customisations should be stored? Because, if I store them in the main Elm file, it should know about all the imported modules customisation settings and also maybe rendering them via Html, and if I store them in the drawing modules then I can’t precisely know which customisation is linked to which variable because I can edit variables on the main Elm file and have customisation settings for, just saying a deleted one. The linking seems too soft for me.

Thanks for your time,
Kristian

I’d recommend watching https://youtu.be/XpDsk374LDE. It talks about file size and splitting into new files.

I like to use the following file structure:

YourProject
  Main.elm --contains all the wiring
  Page.elm --contains the code for routing and shared data for Page
  Page --different Pages
    Loading.elm --Loading page for getting time, randomness, screen size and so on. 
    LoginPage.elm --contains Model,View,Update
    UserPage.elm --contains Model,View,Update
    Admin.elm --contains Model,View,Update
  Data.elm --contains constants and Helper functions for Data
  Data  --Data types
    Config.elm --a config data type
    User.elm -- some User data type and all logic for it
  View.elm --contains constants and Helper functions for View
  View --reusable Views
    Plotter.elm -- contains different views for a Plotter (but no logic)
    BarChart.elm --contains different views for a BarChart (but no logic)
    User.elm --contains different views for a User

A note on Reusable Views: In Elm we try to simplify as much as possible, thus a component is an overkill most of the times. Instead, we use reusable views containing a view, Model, and a lot of helper functions but no update. A often cited example is Evans Sortable Table. A collection of resources regarding that topic can be found here: rofrol/elm-best-practices.md.

1 Like

@wolfadex
Thanks for the recommended video. It already was in my watching list. Watched it now. It looks like an in depth view of what the official guide describes. Those are things I’m trying to follow while developing my learning project.

@Lucas_Payr
Thanks for the file structure info. I noted the official guide also advises to avoid creating components, since they’re overkill most of the times. With that in mind, the best option I can see, guided by your post too, is to have:

  • a Main file which handles the page flow
  • n “view” files which know how to draw (so the main file call them) and do nothing else (“don’t overdo it” style)

The only point I’m not sure about is: should I create a Data file which shares the variables information (1) (eg. name, value, color) or let every drawing implementation to have n options (parameters) to their draw methods (2), for example draw : List Data where the Data is not the same one as in the Main file (so possibly I can have other info such as “position”, “size”, “order”, etc.), and it’s a Main file task to map info correctly, on a per graph base?

Using the (1) option I can’t customize some settings per graph from the Main file, unless the Data structure is enormous enough to contain every possibile settings of each graph file (which seems not right to me).

Using the (2) option I can customize settings per graph, because the Main file, every time it has to draw something, can pass further information to the graph, specifically to each graph (maybe one graph has a position parameter, maybe another one has a order parameter, etc.). Moreover, using this option helps decoupling the graph modules from the main one, essentially making them agnostic of where they’re being used (which seems better to me).

You need to make a clear difference between data and meta-data.

In other words, between the essential information contained in the data set and the accidental information about this information (how it is being presented, colors, size, pagination, etc.).

A specific view will then take the essential data and some kind of configuration (the meta data) specific for that particular view of the essential data.

Thanks pdamoc.

My concern is that a plot, for example, needs a box size, a list of values (with meta data, I would say needed meta data, such as label and color) and possibly other things (plot scale, type of visual representation for the variables, maybe for each variables).

All this metadata, since I don’t want a model and update in the graph modules, should be handled by the main module, which will expose an UI for selecting all this options, based on what it knows it can pass to each graph module “API”.

So, I would consider the second option as a better one. The main file do whatever he wants with his model and the graph modules do whatever they want with the data type they ask for in their exposed drawing functions.

What I can then do internally in the main file is to have right in or transform my model data to those graph specific data and meta data.

Main.elm
View
  Plot.elm -- which will have a PlotData, a PlotMetaData data types etc
  BarChart.elm -- same as above for the plot, or whatever it needs

How does it sounds like?

It sounds OK.

My advice is to trust your intuition and the compiler (you will be able to refactor the code easily).

You will be the first user of those modules and you will learn a lot more from using them.

Thanks a lot, pdamoc!

I’ll now start using it and finish some basics functionality. Surely, I will learn a lot about the modules and how they interoperate, so I can refactor if necessary.

Concepts about easy refactor and how to approach the project structure in Elm are quite different from what I’m used to, but I like them.

Thanks everyone who helped me start the project.