parsonsmatt / kale

A quick, easy, and declarative task runner for Haskell code to destroy your boiler plate!
BSD 3-Clause "New" or "Revised" License
33 stars 5 forks source link

Write documentation #4

Open parsonsmatt opened 7 years ago

parsonsmatt commented 7 years ago

This project needs:

  1. [x] Haddock API documentation.
  2. [ ] Example projects
  3. [ ] Tutorials on getting started
  4. [ ] A solid README
tmciver commented 7 years ago

@parsonsmatt I'd like to take a stab at this since I'm not really sure what this project's purpose is and I figure writing the docs would be a good way to find out. :) Perhaps you could describe the purpose in a little more detail and/or provide an example use case.

Looking at the code it seems to me that this app will generate Haskell source code for user-provided "Tasks" in a particular location that are not known to kale at compile time. These tasks would do . . . what? Anything? This seems to be in the spirit of other command line tools like git, for example, that have sub-commands (git branch, git checkout, etc.). In this case it would be kale runMySpecialCommand or kale runThisOtherSpecialCommand. Am I close?

parsonsmatt commented 7 years ago

That's the idea!

There's a decent amount of boilerplate associated with writing a command line parsing utility, and most of it can be discovered and automated away. Stuff like optparse-generic goes a really long way to making it declarative and gets rid of 99% of the boilerplate, but we can do better :)

The "big idea" is to discover all the modules with a form:

module Lib.FooTask where

data Args = Args { i am command line arguments for the program }

task :: Args -> IO ()
task args = ...

And create a nice command line interface for calling them. What the users end up doing is totally up to them, this library's purpose is to eliminate the boilerplate of:

module Task where

import qualified Lib.FooTask as FooTask
import qualified Lib.BarTask as BarTask
-- ...

data Command = Foo { fooArgs ... } | Bar { barArgs ... } | ...
  deriving (Generic, ParseRecord)

main :: IO ()
main = do
  cmd <- getRecord "the program"
  case cmd of
    Foo  {..} -> FooTask.task FooTask.Args {..}
    Bar {..} -> BarTask.task BarTask.Args {..}
    ...
tmciver commented 7 years ago

Thanks for the speedy response! This is great and is enough to get me started. PR coming your way soon.

tmciver commented 6 years ago

I seem to be having a little trouble with Haddock (I've never used it before). I added several docs to functions in Kale.hs and when I run

$ cabal haddock

I see this output:

Running Haddock for kale-0.1.0.0...
Preprocessing library kale-0.1.0.0...
Warning: The documentation for the following packages are not installed. No
links will be generated to these packages: Only-0.1, ansi-terminal-0.7.1.1,
ansi-wl-pprint-0.6.8.1, colour-2.3.3, optparse-applicative-0.14.0.0,
optparse-generic-1.2.3, semigroups-0.18.3, system-filepath-0.4.13.4,
text-1.2.2.2, transformers-compat-0.5.1.4, void-0.7.2
target ‘command’ is not a module name or a source file

And no documentation is generated. Any ideas?

parsonsmatt commented 6 years ago

I'd suggest using stack for building documentation. stack haddock works for me on the master branch.

If you want to open a PR, I can take a look at it :)

tmciver commented 6 years ago

I have a couple of questions regarding Kale's command line arguments. First, this line takes the first and third command line arguments and ignore the second and fourth. Why is that? What are the second and fourth arguments supposed to be?

On this line it says that Kale doesn't take command line arguments. It seems that this is a misstatement, correct?

parsonsmatt commented 6 years ago

Good questions!

The GHC preprocesser step provides some arguments to the executable that's used to process the source file. I don't know what the arguments do exactly -- I copied the structure from hspec-discover.

The arguments that Kale takes are provided by GHC. It doesn't accept any user arguments.

tmciver commented 6 years ago

OK. That leads to a question I've had about the intended workflow of Kale. Is Kale expected to be executed as part the client project's build? How exactly? It sounds like you intend for it to be run by hspec, correct?

parsonsmatt commented 6 years ago

GHC has a textual preprocessor step that allows you to provide your own way to manipulate the source files. It calls the executable and that executable writes a Haskell source file to disk, which GHC then compiles as usual.

So kale gets called in this step, discovers all the task modules, and then writes a Haskell file that GHC compiles and runs.

tmciver commented 6 years ago

@parsonsmatt Thanks for the info. I did not know that GHC had such a textual preprocessing step and some quick googling only seems to lead me to CPP-related stuff but I suspect that is not what's at play here. Can you give more information about this or perhaps a link? In particular, I'm interested to know how you "provide your own way to manipulate the source files." Where do you tell GHC to call Kale's main function?

parsonsmatt commented 6 years ago

This file in the example is where it is called. I found the pgmF flag thanks to hspec and it's discovery feature.

tmciver commented 6 years ago

I've created test client application for Kale here but when I run stack build, I get the following error:

$ stack build
kaletest-0.1.0.0: configure (lib + exe)
colour-2.3.4: download
kaletest-0.1.0.0: build (lib + exe)
Only-0.1: configure
Only-0.1: build
semigroups-0.18.3: configure
semigroups-0.18.3: build
colour-2.3.4: configure
colour-2.3.4: build
semigroups-0.18.3: copy/register
Only-0.1: copy/register
colour-2.3.4: copy/register
Log files have been written to: /home/tim/workspace/haskell/kaletest/.stack-work/logs/
Progress: 4/13
--  While building custom Setup.hs for package kaletest-0.1.0.0 using:
      /home/tim/.stack/setup-exe-cache/x86_64-linux/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-linux/Cabal-2.0.1.0 build lib:kaletest exe:kaletest-exe --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1
    Logs have been written to: /home/tim/workspace/haskell/kaletest/.stack-work/logs/kaletest-0.1.0.0.log

    Configuring kaletest-0.1.0.0...
    Preprocessing library for kaletest-0.1.0.0..
    Building library for kaletest-0.1.0.0..
    ["src/Lib.hs","src/Lib.hs","/tmp/ghc456_0/ghc_1.hspp"]
    [2 of 3] Compiling Lib              ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/Lib.o )

    /home/tim/workspace/haskell/kaletest/src/Lib.hs:10:1: error:
        Could not find module ‘Kale.Discover’
        Use -v to see a list of the files searched for.

It seems that GHC is looking for a Haskell module and not an executable named kale-discover as I'm expecting. Any idea what I'm doing wrong?

parsonsmatt commented 6 years ago

You need kale in the dependencies of your project.

Matt Parsons

On Mon, Jan 22, 2018 at 10:46 AM, Tim McIver notifications@github.com wrote:

I've created test client application for Kale here https://github.com/tmciver/kaletest but when I run stack build, I get the following error:

$ stack build kaletest-0.1.0.0: configure (lib + exe) colour-2.3.4: download kaletest-0.1.0.0: build (lib + exe) Only-0.1: configure Only-0.1: build semigroups-0.18.3: configure semigroups-0.18.3: build colour-2.3.4: configure colour-2.3.4: build semigroups-0.18.3: copy/register Only-0.1: copy/register colour-2.3.4: copy/register Log files have been written to: /home/tim/workspace/haskell/kaletest/.stack-work/logs/ Progress: 4/13 -- While building custom Setup.hs for package kaletest-0.1.0.0 using: /home/tim/.stack/setup-exe-cache/x86_64-linux/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-linux/Cabal-2.0.1.0 build lib:kaletest exe:kaletest-exe --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always" Process exited with code: ExitFailure 1 Logs have been written to: /home/tim/workspace/haskell/kaletest/.stack-work/logs/kaletest-0.1.0.0.log

Configuring kaletest-0.1.0.0...
Preprocessing library for kaletest-0.1.0.0..
Building library for kaletest-0.1.0.0..
["src/Lib.hs","src/Lib.hs","/tmp/ghc456_0/ghc_1.hspp"]
[2 of 3] Compiling Lib              ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/Lib.o )

/home/tim/workspace/haskell/kaletest/src/Lib.hs:10:1: error:
    Could not find module ‘Kale.Discover’
    Use -v to see a list of the files searched for.

It seems that GHC is looking for a Haskell module and not an executable named kale-discover as I'm expecting. Any idea what I'm doing wrong?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/parsonsmatt/kale/issues/4#issuecomment-359506338, or mute the thread https://github.com/notifications/unsubscribe-auth/AG-LIBklH1kJ0x8FOxWMDZDHss6vOSXQks5tNMmLgaJpZM4QhaGW .

tmciver commented 6 years ago

I see. I added kale as a dep to package.yaml and it builds without error now. I was under the impression that the preprocessor specified in {-# OPTIONS_GHC -F -pgmF my-pre-processor #-} could be any old executable on the path in which case the kaletest project would not need to depend on kale at all. Can you tell me why I need to set kale as a dependency?

Also, when stack build completed I expected to see a new file generated by the preprocessor. Is this not a correct expectation? Is that generated file ephemeral?

parsonsmatt commented 6 years ago

I don't understand the pgmF flag or how exactly it works. I coped code from hspec :slightly_smiling_face:

Yes, the generated file only exists temporarily while GHC compiles and builds it into the executable.

tmciver commented 6 years ago

I'm starting to understand this pgmF business a bit better. One thing I tried in an effort to understand things better was to specify these GHC options on the command line instead of in a Lib.hs file like so:

$ stack build --ghc-options="-F -pgmF kale-discover"

but it gives this error:

/home/tim/workspace/haskell/kaletest/src/Lib/Task.hs:8:8: error:
        File name does not match module name:
        Saw: ‘Task’
        Expected: ‘Lib.Task’

This seems to ultimately be due to the way kale generates the module name for the generated Haskell (in the pathToModule function) which only takes the final segment of the path as the module name. This seems to work since the current method of preprocessing is done at the same level as the Lib directory so the generated module is directly under src. (The above method apparently does the preprocessing in the same directory as the module, which makes sense.)

So I wonder if you have a preference for how users might specify the GHC options. You could leave things as is and just tell the user how to specify the options or change things so that the options could be specified in other ways.

parsonsmatt commented 6 years ago

Interesting. The -pgmF flag when used liket hat is likely going to apply the preprocessor to all of the files in the project, which isn't what we want.

I would definitely prefer folks to write the GHC options in the file that is going to become the module containing the task executable stuff -- in the same way that hspec-discover only is ever used like

-- test/Spec.hs
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
tmciver commented 6 years ago

Good enough! Yes, I had thought about how that command would run the preprocessor on all the files but forgot to mention it. For that reason I agree that it's a good idea to specify the options as you are doing.