Open ssadler opened 11 years ago
I propose something like the following. Note that more granularity may be required in practice, but here is the separation of modules.
One important detail: Main
and *Frontend*
never import
the rest of the codebase. This is because (I experienced) that it confuses GHC
because GHC encounters duplicate symbols and doesn't know what to do. So
everything that runs within the frontend is in a Frontend module.
Main
Executable, entry point of hsl binary.
System.Console.HSL.Frontend.*
Everything to do with the hsl binary.. Including compilation, user config
discovery, command line flags.
System.Console.HSL.Core.Types.*
Top level runtime API, no instances
System.Console.HSL.Core.Data.*
Instances for data marshalling
System.Console.HSL.Core.Run.ByteString
System.Console.HSL.Core.Run.Text
Runner functions (produce input, print output)
System.Console.HSL.Exts.Magic.*
System.Console.HSL.Exts.Magic.Frontend
Runner functions for input inferencing
(Below are possiblities)
System.Console.Hsl.Exts.Daemon
Quickstart (?) daemon.
System.Console.Hsl.Exts.Export
Command line utility to produce a binary which runs the expression instead of
actually running it.
System.Console.Hsl.Exts.Cloud
Functions to interact with cloud
TL;DR Main System.Console.HSL.Frontend System.Console.HSL.Core System.Console.HSL.Exts
One important detail:
Main
and*Frontend*
never import the rest of the codebase.
I'm confused. What use is the rest of the codebase if it not to be imported by Main in order to run things? Maybe you mean that Main should not be imported by the rest of the codebase?
Top level runtime API, no instances
Won't that force us to create orphan instances elsewhere?
Command line utility to produce a binary which runs the expression instead of actually running it.
Brilliant! It's so easy to do, and it would encourage users to create small tools and name them instead of crafting longer and longer haskell expressions.
By the way, I don't think that the module hierarchy matters. We are building an executable, not a library, so it's not a user-facing part of our product. I think whoever implements a feature should put it wherever, and then we can rename and refactor as needed.
I'm confused. What use is the rest of the codebase if it not to be imported by Main in order to run things? Maybe you mean that Main should not be imported by the rest of the codebase?
The codebase isn't imported by Main directly, it's added to an interpreter context before evaluating an expression. It doesn't need to be imported into Main, only frontend related logic does.
Won't that force us to create orphan instances elsewhere?
I guess that's what I had in mind, yes... Would it be bad? I'm genuinely not quite sure of the effect. But I thought it would be neat if instances can be added anywhere without needing to be collected in one place, isn't this normal?
An orphan instance is a type class instance for class C and type T which is neither defined in the module where C is defined nor in the module where T is defined.
I was thinking to put a collection of the basic instances in Core.Data and then extra instances as required around the code. For example, JSON is a separate self contained module, instances that it requires are defined there.
By the way, I don't think that the module hierarchy matters. We are building an executable, not a library, so it's not a user-facing part of our product. I think whoever implements a feature should put it wherever, and then we can rename and refactor as needed.
Fair point.
The codebase isn't imported by Main directly, it's added to an interpreter context before evaluating an expression. It doesn't need to be imported into Main, only frontend related logic does.
Makes a lot more sense now, thank you.
I think we should define terms to distinguish the codebase used by the tool from the codebase interpreted by the tool. To me, "frontend" means the part which reads the command-line arguments, interprets the flags, prints the help, and passes the expression to the "backend" for the heavy-lifting. But the backend is still used by the tool, as this backend could decide, for example, to run the expression multiple times, as will be required for the magic stuff. I think the part which is interpreted by the tool should be called the "runtime".
Gelisam posted: