chshersh / iris

🌈 Haskell CLI Framework supporting Command Line Interface Guidelines
https://hackage.haskell.org/package/iris
Mozilla Public License 2.0
176 stars 21 forks source link

Rethink 'CliEnvSettings' and 'defaultCliEnvSettings' #67

Closed chshersh closed 1 year ago

chshersh commented 1 year ago

Currently, the CliEnvSettings data type is defined in the following way:

https://github.com/chshersh/iris/blob/0b9c50da0a277b298c62064929eb2f875c902841/src/Iris/Env.hs#L53-L71

And defaultCliEnvSettings:

https://github.com/chshersh/iris/blob/0b9c50da0a277b298c62064929eb2f875c902841/src/Iris/Env.hs#L78-L86

The idea of using () in the type signature was to rely on type-changing record updates. Unfortunately, the following code doesn't compile:

cmdP :: Parser Cmd
cmdP = ...

appSettings :: Iris.CliEnvSettings Cmd ()
appSettings = Iris.defaultCliEnvSettings
    { Iris.cliEnvSettingsCmdParser = cmdP
    }

It fails with the error:

... location ...
    • Couldn't match type ‘()’ with ‘Cmd’
      Expected: CliEnvSettings Cmd ()
        Actual: CliEnvSettings () ()
    • In the expression:
        Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
      In an equation for ‘appSettings’:
          appSettings
            = Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
   |
26 | appSettings = Iris.defaultCliEnvSettings
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^...

... location ...
    • Couldn't match type ‘Cmd’ with ‘()’
      Expected: Options.Applicative.Types.Parser ()
        Actual: Options.Applicative.Types.Parser Cmd
    • In the ‘cliEnvSettingsCmdParser’ field of a record
      In the expression:
        Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
      In an equation for ‘appSettings’:
          appSettings
            = Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
   |
27 |     { cliEnvSettingsCmdParser = cmdP
   |                                 ^^^^

The problem here because the cliEnvSettingsRequiredTools is also parametrised by cmd. So to change the type, you need to update both fields like this:

appSettings :: Iris.CliEnvSettings Cmd ()
appSettings = Iris.defaultCliEnvSettings
    { Iris.cliEnvSettingsCmdParser = cmdP
    , Iris.cliEnvSettingsRequiredTools = []
    }

Which is a shame. You need to set an extra field you don't care about all the time because you want to change the type of the CLI command.

It would be great to improve this interface. However, I don't have ideas at the moment. This requires some thinking 🤔

chshersh commented 1 year ago

Okay, I was thinking about the interface and here is the proposed solution:

So, instead of specifying a global list of tools, users can call the need function in the required commands:

app :: App ()
app = Iris.asksCliEnv Iris.cliEnvCmd >>= \case
    Download url -> do
        need ["curl"]
        runDownload URL
    Evaluate hs -> do
        need ["ghc", "cabal"]
        runEvaluate hs
german1608 commented 1 year ago

Is anyone working on this? I can take a look otherwise 😄

chshersh commented 1 year ago

@german1608 I believe no one is looking (or at least haven't written about this explicitly). So feel free to work on this 🙂