Closed basile-henry closed 4 years ago
To give a more concrete use case where we might want multi-line commands:
The Dhall REPL uses the :let
command to add bindings to the environment. Dhall also has mutli-line strings, so in order to bind a multi-line string to a variable we can write the following:
> :let multiline = ''
| Hello
| World
| ''
Before this PR, this unfortunately did not work (and didn't actually parse) because all the newlines got replaced by spaces.
Not sure how I feel about this. This forces all the command functions which previously pattern matched on [String]
to now pattern match of String
and rehydrate the words back into a list if needed. While it might be convenient in this example quite a lot of my existing users do something like this which would all have to be refactored after this patch.
symbols :: [[Char]] -> Repl ()
symbols [] = do
... -- no arguments
symbols [x] = do
... -- one arguments
symbols xs = do
... -- n arguments
symbols :: [Char] -> Repl ()
symbols arg = do
case words arg of
[] -> ...
[x] -> ...
xs -> ...
I understand your concern about changing this part of the API. My view of how the library is being used is very much biased by its use in the Dhall REPL, where basically every command ended up doing unwords
on the arguments.
I had a quick look at repline
's (public) reverse dependencies to see how the arguments were usually handled: https://packdeps.haskellers.com/reverse/repline
dhall
: Uses unwords
for most/all commands: https://github.com/dhall-lang/dhall-haskell/blob/4aabe1cd3f2518ba9d77669366d24531db6c100a/dhall/src/Dhall/Repl.hs#L167hnix
: Also seems to be using unwords
: https://github.com/haskell-nix/hnix/blob/d1ff97dd1ea0855612dd2b8e316c7b2b29dd5602/main/Repl.hs#L154hwhile
: Uses intercalate " "
: https://github.com/alexj136/HWhile/blob/036e84dd394ece5a838dad81908f632e5cfa5311/src/lib/REPL.hs#L113libraft
: The github repo doesn't seem available (maybe private?), the source is on hackage but difficult to quickly search through.zre
: They don't use commands and instead roll out their own command parsing: https://github.com/sorki/haskell-zre/blob/0128ed46fcac35a7f67d549dff25598f0a7de8ae/app/Main.hs#L65If we do want to keep the arguments split by words, here are a few possible API we could provide:
data Args = SingleLineArgs [String] | MultiLineArgs String
type Cmd m = Args -> m ()
type Args = [[String]] -- split by lines, split by words
type Cmd m = Args -> m ()
System.Console.Repline.Multiline
and make the multi-line command a compulsory part of the options in this module.I am very much open to changing this PR if you like any of my suggestions or if you have other suggestions about what the API should look like. I think a good stable API is very valuable! :smile:
Note: If you are interested in just how much 0.4 + this PR requires to be changed here is the current draft PR for Dhall that implements this change https://github.com/dhall-lang/dhall-haskell/pull/1867
Ok, will just have to document migration from 0.3 to 0.4 well enough and it should be fine. I suppose some refactoring using words
and unwords
isn't that much overhead.
While commands already work with multi-line inputs, the commands cannot distinguish between a space and newline character since the arguments the function receives are already split by word.
This commit changes this and provides the raw argument string to the command function.