Quick way to know the valid values of a function parameter (find all values under a concrete type)

Sorry for not well-described question in advance, if there’s anything unclear, please leave a reply!

Brief question

What I would like to ask is that:

How to know the valid options for a parameter?

For exmaple, if I want to use a specific function, and it said that its first parameter is restricted to be of type IO, then how could I knew that the possible options would be stderr, stdout, stdin?

Detailed description for my question

Suppose now I want to use a function / type…, say ConsoleLogger(), I could get its definition by REPL’s help mode / hover help in IDE to check the documentation, for example, in the REPL help mode, we have:

help?> ConsoleLogger
search: ConsoleLogger

  ConsoleLogger(stream=stderr, min_level=Info; meta_formatter=default_metafmt,
                show_limited=true, right_justify=0)

# some more descriptions

I would then wanted to know what else values I could choose for the parameter stream, in the IDE it would show field types, and in the REPL we could search by fieldtypes():

julia> fieldtypes(ConsoleLogger)
(IO, Base.CoreLogging.LogLevel, Any, Bool, Int64, Dict{Any, Int64})

Now I knew that stream should be an instance of IO, but seraching for IO in the help mode would only show its subtypes:

help?> IO
search: IO IOStream IOBuffer IOContext fdio Union union union! Union{} UnionAll Rational

  No documentation found.

  Summary
  ≡≡≡≡≡≡≡≡≡

  abstract type IO <: Any

  Subtypes
  ≡≡≡≡≡≡≡≡≡≡

  Base.AbstractPipe
  Base.DevNull
  Base.Filesystem.AbstractFile
  Base.GenericIOBuffer
  Base.LibuvStream
  Base.SecretBuffer
  Base64.Base64DecodePipe
  Base64.Base64EncodePipe
  Core.CoreSTDERR
  Core.CoreSTDOUT
  IOStream
  Mmap.Anonymous

But from this result, I could now know whether each of the listed things are abstract types or concrete types, I would need to deep further to check for each listed things, then even until I found a concrete type, I still could not know what values are under this concrete type.

I’m new to Julia and I haven’t found related solution from the julia docs and forum posts, if this could be found by some keyword or I missed some part from the docs, please let me know and sorry for asking repeated question!

Welcome to the Julia discourse forum :waving_hand:

There is subtypes

julia> subtypes(IO)
12-element Vector{Any}:
 Base.AbstractPipe
 Base.DevNull
 Base.Filesystem.AbstractFile
 Base.GenericIOBuffer
 Base.LibuvStream
 Base.SecretBuffer
 Base64.Base64DecodePipe
 Base64.Base64EncodePipe
 Core.CoreSTDERR
 Core.CoreSTDOUT
 IOStream
 REPL.LimitIO

this lists all subtypes of a type at the type of asking, since loading another package might introduce further such types.

Note that this is about types not values – for example Float64 would be the type 1.23 its value, so be also careful with terminology.

1 Like

Thank you for your reply!

However, I think it’s that English is not my native language so I’m not expressing my question well. What I would like to know is that, using the same example:

Now I knew that the type that I’m required to use for parameter stream in ConsoleLogger() is abstract type IO, and I could know its subtypes as you mentioned, but how do I know for example, stream could be stdout, stderr, stdin…?

I found these options by reading the Julia docs, as shown in the following image:

But I wonder if there’s some other way that could quickly search these available options (maybe from some REPL command or extensions for IDE)

If this information is not in the docstring (which are often imperfect), this is the kind of question that LLMs are pretty good at answering.

Thank you for your reply!

Actually in the example that I mentioned above, I found that stdout, stderr, stdin… are the options by asking LLM, I’m just trying to not rely on getting the answer by LLM that much, and thought that maybe there’s a way to know these details from some Julia’s internal functionality.

Anyways, thank you for telling me that this is just not available!

In this specific case it’s actually quite easy to get the sort of options you’ve described:

julia> [name for name in names(Base) #=only public names=# if getglobal(Base, name) isa IO]
4-element Vector{Symbol}:
 :devnull
 :stderr
 :stdin
 :stdout

help?> devnull
search: devnull

  devnull

  Used in a stream redirect to discard all data written to it. Essentially equivalent to /dev/null on Unix or NUL on Windows. Usage:

  run(pipeline(`cat test.txt`, devnull))

help?> stderr
search: stderr sortperm step stdout stdin err

  stderr::IO

  Global variable referring to the standard error stream.

help?> stdin
search: stdin string strip sin setdiff Cstring lstrip Cwstring stride rstrip splitdir stdout stderr String setdiff! sprint

  stdin::IO

  Global variable referring to the standard input stream.

help?> stdout
search: stdout struct stat sort stdin stderr

  stdout::IO

  Global variable referring to the standard out stream.

but public global variables only bind a small fraction of possible inputs. If you’re avoiding LLM hallucinations, then the documentation better be good. Runtime reflection does help, but it’s not feasible to completely rely on it because it’s not structured in a way one person would try to communicate to another. For example, IOBuffer is very useful for string processing, but it’s an alias for a subtype of the printed IO subtype Base.GenericIOBuffer{T<:AbstractVector{UInt8}}, and there’s no way for you to know how those types are useful just from names.

I see. I do not fully agree with Cédric that doc strings are often imperfect, but sometimes they are.

One thing we could actually add, is that the doc string for IO could list the global variables like stdin as examples. That would maybe help with discoverability here.
One thing we often can do is improve docs with Pull Requests :slight_smile:

1 Like