I know the REPL
is probably the least popular interface to Swift (as compared to Xcode projects, playground, iOS playground, swiftc
/swift
, and sharable online code "playgrounds"), but I think we should at least make some minimal effort to support a reasonable user experience.
We have a bit of a vi
problem. Exiting the REPL is quite difficult. ctrl+d is a completely intuitive shortcut. Even if you knew you need to send EOF (as a newb not initiated with the backstory of terminal emulators, you don't), ctrl+d is completely unintuitive ("EOF".contains("d") == false
) shortcut.
Consider these typical examples of what people might try to type to access help, or to quit.
Without Foundation imported
A typical novice might think "ctrl+c is how you quit terminal programs, right?" Nope.
1> ^C
1> ^C
1> ^C
Silent failures, love it.
Help? Nope.
1> help
error: repl.swift:1:1: error: use of unresolved identifier 'help'
help
^~~~
1> --help
error: repl.swift:1:3: error: use of unresolved identifier 'help'
--help
^~~~
1> \help
error: repl.swift:1:2: error: use of unresolved identifier 'help'
\help
^~~~
error: repl.swift:1:2: error: invalid component of Swift key path
\help
^
error: repl.swift:1:1: error: expression type 'WritableKeyPath<_, _>' is ambiguous without more context
\help
^~~~~
1> ?
error: repl.swift:1:1: error: expected expression
?
^
1> exit
error: repl.swift:1:1: error: use of unresolved identifier 'exit'
exit
^~~~
1> quit
error: repl.swift:1:1: error: use of unresolved identifier 'quit'
quit
^~~~
With Foundation
imported
This is where it gets interesting. The help
and ?
responses are the same, but exit
is interesting:
exit
on its own evaluates to an unapplied closure of type (Int32) -> Never
. I'm sure everybody knows what 0x00000001000c6c80 $__lldb_expr11@nonobjc __C.exit(Swift.Int32) -> Swift.Never at repl10-fef73e..swift
means, right? /s
1> import Foundation
2> exit
$R0: (Int32) -> Never = 0x00000001000c6c80 $__lldb_expr11`@nonobjc __C.exit(Swift.Int32) -> Swift.Never at repl10-fef73e..swift
Naively trying exit()
obviously won't work, because you're missing the exist code as a Int32
argument. This is clear enough, but to a novice, it's not really obvious what this mystical number is, why it's required.
1> import Foundation
2> exit()
error: repl.swift:6:6: error: missing argument for parameter #1 in call
exit()
^
<#Int32#>
Darwin.exit:1:13: note: 'exit' declared here
public func exit(_: Int32) -> Never
The results they yield make sense, from a strictly Swift perspective, in that they're not valid syntax, and the compiler is doing its best to handle the malformed Swift syntax. But I think it's abundantly clear that we should incorporate some special case syntax to that's discoverable and obvious, to support our users.
Compare how other languages do it:
Other languages set a much higher bar for user friendliness in this regard, and I think it would be a benefit to our community to look up to them as roll models, in this regard:
Ruby
Click to expand
quit
and exit
both exit right away. It's exactly what it says on the tin.
help
is obvious, accessible, and interactive:
irb(main):001:0> help
Enter the method name you want to look up.
You can use tab to autocomplete.
Enter a blank line to exit.
>> # *awaits your input here*
I'll dock points for ctrl+c
irb(main):001:0> ^C
irb(main):001:0>
Python
Click to expand
exit
doesn't work directly, but at least it has a very clear message to help you:
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
quit
is similar:
>>> quit
Use quit() or Ctrl-D (i.e. EOF) to exit
help
is wonderful:
$ python3
Python 3.6.5 (default, Mar 30 2018, 06:42:10)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>> help()
Welcome to Python 3.6's help utility!
If this is your first time using Python, you should definitely check out
the tutorial on the Internet at https://docs.python.org/3.6/tutorial/.
Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".
To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics". Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".
help> # *awaits your input here*
If you press enter, or type quit
at this prompt, you get this very helpful message:
You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)". Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
But on the down side, the handling of ctrl+c is even worse than Ruby and Swift:
>>> # *I type ctrl-c here*, nothing like is printed (e.g. like "^C" in Ruby and Swift)
KeyboardInterrupt
Bash
Click to expand
exit
is straight forward:
bash-3.2$ exit
exit
Unfortunately, quit
doesn't work, even though an alias could have easily been made :
bash-3.2$ quit
bash: quit: command not found
help
is a bit cryptic if you won't know the syntax they use for describing optional vs manditory options, but at least it's quit thorough:
bash-3.2$ help
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
These shell commands are defined internally. Type `help' to see this list.
Type `help name' to find out more about the function `name'.
Use `info bash' to find out more about the shell in general.
Use `man -k' or `info' to find out more about commands not in this list.
A star (*) next to a name means that the command is disabled.
JOB_SPEC [&] (( expression ))
. filename [arguments] :
[ arg... ] [[ expression ]]
alias [-p] [name[=value] ... ] bg [job_spec ...]
bind [-lpvsPVS] [-m keymap] [-f fi break [n]
builtin [shell-builtin [arg ...]] caller [EXPR]
case WORD in [PATTERN [| PATTERN]. cd [-L|-P] [dir]
command [-pVv] command [arg ...] compgen [-abcdefgjksuv] [-o option
complete [-abcdefgjksuv] [-pr] [-o continue [n]
declare [-afFirtx] [-p] [name[=val dirs [-clpv] [+N] [-N]
disown [-h] [-ar] [jobspec ...] echo [-neE] [arg ...]
enable [-pnds] [-a] [-f filename] eval [arg ...]
exec [-cl] [-a name] file [redirec exit [n]
export [-nf] [name[=value] ...] or false
fc [-e ename] [-nlr] [first] [last fg [job_spec]
for NAME [in WORDS ... ;] do COMMA for (( exp1; exp2; exp3 )); do COM
function NAME { COMMANDS ; } or NA getopts optstring name [arg]
hash [-lr] [-p pathname] [-dt] [na help [-s] [pattern ...]
history [-c] [-d offset] [n] or hi if COMMANDS; then COMMANDS; [ elif
jobs [-lnprs] [jobspec ...] or job kill [-s sigspec | -n signum | -si
let arg [arg ...] local name[=value] ...
logout popd [+N | -N] [-n]
printf [-v var] format [arguments] pushd [dir | +N | -N] [-n]
pwd [-LP] read [-ers] [-u fd] [-t timeout] [
readonly [-af] [name[=value] ...] return [n]
select NAME [in WORDS ... ;] do CO set [--abefhkmnptuvxBCHP] [-o opti
shift [n] shopt [-pqsu] [-o long-option] opt
source filename [arguments] suspend [-f]
test [expr] time [-p] PIPELINE
times trap [-lp] [arg signal_spec ...]
true type [-afptP] name [name ...]
typeset [-afFirtx] [-p] name[=valu ulimit [-SHacdfilmnpqstuvx] [limit
umask [-p] [-S] [mode] unalias [-a] name [name ...]
unset [-f] [-v] [name ...] until COMMANDS; do COMMANDS; done
variables - Some variable names an wait [n]
while COMMANDS; do COMMANDS; done { COMMANDS ; }
ctrl+c just makes fresh new lines, which is acceptable given that bash is a shell, not just a REPL, so the expectations are a bit different
bash-3.2$
bash-3.2$
ctrl+d displays the text exit
and then exits.
bash-3.2$ exit