Open tcNickolas opened 10 months ago
This was a source of some debate when string interpolation was first added to the new stack, as we weren't sure there was value in being able to convert all types into strings. Notably, callables and type-parametrized variables are not convertible to strings, and UDTs were added later and not explicitly given support for string conversion. I definitely think it's worth decided how to display UDTs (possibly via printing them purely as tuples the same way they would be if unwrapped). But that doesn't address the second point of being able to support generics in string interpolation. Perhaps that warrants its own separate issue for discussion?
There absolutely is value in converting data types into strings! In another project (I haven't updated that to Modern QDK yet) I'm using string representation of a callable (its name) in a demo where I run the same code for different operations and their eigenvectors/eigenvalues and print the name to show which one I'm doing right now. ToStringWithPrefix
is admittedly a very contrived example that I came up with to illustrate generics, I don't need it for real work, but the demo that prints operation name has a real-life practical use.
I'm not sure about lambdas and qubits, but other than those two, I think other data types have natural representations and should be convertible to strings.
Any updates on converting callables into strings? I ended up using a workaround for Complex and CompexPolar types in the ComplexArithmetic kata, though I still want to convert UDTs into strings the way we do with tuples, but I can't think of a nice workaround for callables.
We haven't worked on this yet, sorry @tcNickolas. UDTs and callables that are defined globally should be possible to handle in a coherent way, but the complexity comes in with lambdas, which are lifted into globals during compilation but don't have any well known names. Do you have any suggestions for how you would want those to be displayed?
In my scenarios I need callables that are defined globally, I don't really expect a reasonable name printed for a lambda. In Classic QDK, an internal name was printed:
operation HelloQ () : Unit {
let a = q => H(q);
Message($"{a}");
}
would yield _10a961aab4994242bcee705de78cc704_HelloQ{_}
I can imagine two ways to approach this:
lambda (Qubit => Unit is Adj + Ctl)
lambda
: q => H(q)
or lambda: q => H(q)
I like the second one more, it's more informative.
CONTRIBUTORS PLEASE READ
Welcome! Please read carefully if you'd like to contribute a fix to this issue.
For this issue we're looking for a way to support callables inside interpolated strings in Message()
s. The below code sample should run without errors.
namespace MyQuantumProgram {
@EntryPoint()
operation Main() : Unit {
mutable x = MyType;
Message($"current value of x = {x} should match {MyType}");
set x = MyFunction;
Message($"current value of x = {x} should match {MyFunction}");
mutable y = MyOperation;
Message($"current value of y = {y} should match {MyOperation}");
set y = (Int, Bool) => MyType(1, false);
let z = y;
Message($"current value of y = {y} should match {z}");
}
newtype MyType = (Int, Bool);
operation MyOperation(i : Int, b : Bool) : MyType { MyType(i, b) }
function MyFunction(i : Int, b : Bool) : MyType { MyType(i, b) }
}
Getting started
Take a look through our README to orient yourself in the repo and find instructions on how to build.
For this issue, you'll want to have a working knowledge of Rust and compilers.
The first part of the fix is changing the type system so that callables are accepted inside interpolated strings. In other words, making the code above compile without errors. The below is a starting point: https://github.com/microsoft/qsharp/blob/cfab56cc00d1415566c70b10765f1e0b4a71ad81/compiler/qsc_frontend/src/typeck/infer.rs#L1007
The second part would be coming up with an actual string representation for the callable used in the string, so that the Message
outputs in the above sample look correct. For function
s and operations
, the fully qualified name could be a good idea. Lambda expressions might be more complicated, but any unique and consistent name would work. (See the discussion above for thoughts on reasonable names). For this part, you may want to take a look at how string components are evaluated at runtime:
https://github.com/microsoft/qsharp/blob/cfab56cc00d1415566c70b10765f1e0b4a71ad81/compiler/qsc_eval/src/lib.rs#L790
Testing
You can demonstrate that the feature works by running the playground locally (see the code example in the issue description).
Please add unit tests verifying the functionality you implemented.
Before you submit a pull request please run python ./build.py
to ensure the project builds cleanly. See README for details.
Reviews
Please ensure your code is tested, clean and well refactored before opening a pull request. If you're not sure, feel free to open a draft PR for preliminary feedback.
Once you have published your PR, the codeowners will automatically get tagged and we'll review shortly.
If you need clarification on an issue please tag @minestarks with your questions.
Describe the bug
Not all data types can be converted to String using string interpolation. I've noticed this for UDTs, with both named and unnamed items, there might be other types affected. This also means that certain types of type-parameterized functions can no longer be implemented, compared to Q# 0.*.
To Reproduce
The following code doesn't work for UDTs:
(If I unwrap them, though, using
ap!
andnp!
, the resulting tuples will be printed)The following type-parameterized function used to work in Q# 0.* but fails to compile now.
Expected behavior
I expect any Q# type to be convertible to String via string interpolation.
System information