ssadler / hawk

Awk for Hoodlums
BSD 3-Clause "New" or "Revised" License
35 stars 2 forks source link

Module layout #13

Open ssadler opened 11 years ago

ssadler commented 11 years ago

Gelisam posted:

We need a module called Magic in which I can put my type-interference magic :) But it should not be a top-level module, to play nice with hackage.

Which other modules do we need?

Here is everything I can think of:

System.Console.Hsl.Main         -- the command-line tool's main entry point, delegates as much work as possible to the other modules
System.Console.Hsl.Main.Opt     -- for documenting and parsing flags
System.Console.Hsl.Main.Config  -- everything ~/.hsp (or ~/.hsl)
System.Console.Hsl.Magic        -- type-inference magic, top-level interface
System.Console.Hsl.Magic.*      -- type-inference magic implementation, many modules
System.Console.Hsl.Process      -- launch ghc, and manage its input/output
System.Console.Hsl.Daemon       -- one day, this will contain functions to interact with the daemon.
System.Console.Hsl.Daemon.*     -- various modules implementing the daemon (it's probably going to require a few)

I'm sure I forgot tons of stuff. But what?

ssadler commented 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

gelisam commented 11 years ago

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.

gelisam commented 11 years ago

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.

ssadler commented 11 years ago

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.

gelisam commented 11 years ago

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".