diff --git a/meetings/2014/LDM-2014-02-10.md b/meetings/2014/LDM-2014-02-10.md new file mode 100644 index 0000000..64a615c --- /dev/null +++ b/meetings/2014/LDM-2014-02-10.md @@ -0,0 +1,244 @@ +VB Language Design Meeting 2014-02-10 + +# Strict Module + +C# lets you have "static class" but VB only has "Module" which lifts all its members into the namespace. Discussion on this was prompted by a comment of MVP Jan Záruba (Đonny) who said +> I use Partial Private Sub New to achieve the same effect as in C# with static class. The advantage of this compared to Module is that static class does not "pollute" namespaces with it's members (and I also cannot have generic module anyway)." + +Đonny encountered a known and approved Roslyn breaking change, where VS2013 let you write "private partial sub new", but Roslyn doesn't. This is by design. But the question is, what could he and other users do? + +Language designer Anthony D. Green looked into it: +> Today VB’s modules behave very much like F#’s modules when the AutoOpen attribute is applied. I’m proposing adding a modifier “Explicit” to modules which will go the other way. Additionally, we’d considered changing the codegen of VB Modules to exactly match those of C# (both abstract and sealed) so that they could be recognized by C# in Dev10. Unfortunately this turned out to be a breaking change due to a bug in the XmlSerializer. With my proposal we could consider changing the metadata representation to match C# and could also map C# static classes back to Explicit Modules. If C# does end up accepting the static import feature but retains the restriction to static classes it’ll be important to have a way for VB code to easily create such things – otherwise C# won’t be able to consume them (e.g. the Roslyn VB SyntaxFactory module). + +## IL + +C# static classes are currently emitted as `.class private abstract auto ansi sealed beforefieldinit` +VB modules are currently emitted as `[StandardModule] .class private auto ansi sealed` + +## Language design + +* How do you declare something whose name is hidden and whose members are lifted to the enclosing namespace? +** VB: +** C#: not available +** F#: not available +* How do you declare something where people have to qualify member access, unless they specifically import them all? +** VB: **NOT POSSIBLE, BUT DESIRABLE** +** C#: new C#/Roslyn feature, "static usings" +** F#: this is the default +* How do you declare something which *always* requires qualification to access members? +** VB: not available +** C#: this is the default +** F#: [] + +There's a clear VB parity gap here, which has been asked-for many times on user-voice and connect. + +Anthony's proposal is for "static classes", no more, no less. The question is (1) whether to allow it, (2) whether to call it "explicit module" or "shared class" or something. + +RESOLUTION: yes, we should allow it. + +## Syntax + +We need a new keyword or keyword pair. Here are the candidates we came up with. + +**Strict Module** +MustQualify Module +Explicit Module +Opaque Module +MustQualifyMember Module +Closed Module +Protected Module +Private Module +Static Module +Greedy Module +Shared Module +Shared Class +Namespace Module + +RESOLUTION: We'll use "strict module" as the best of a bad bunch. We're open to suggestions for better names. + +## Semantics + +The metadata we emit for "Strict Module" should be the same as the metadata for C# static classes. + +Q. Generics? We could say "strict modules" can be generic. Note: you also can't have generic modules in VB. +RESOLUTION: Strict modules CAN be generic; however generic modules CANNOT contain extension methods. + +Q. Nesting? Currently VB modules can't be nested. C# static classes can be nested. +RESOLUTION: Strict modules cannot be nested. + +Q. For C#/Roslyn new feature, when they import static types, will they also allow importing VB modules? +RESOLUTION: up to C# + +Q. How about VB importing C# static classes? +RESOLUTION: Currently, VB can import C#'s static classes, and if they come with [StandardModule] then we auto-open. We will continue to do this. NOTE: THIS IS A BREAKING CHANGE: e.g. you will no longer be able to declare variables of type "Console". Doesn't seem a very bad breaking change. + +Q. What about ? +A. Intellisense will ignore this attribute on strict modules. + + + +--- + +On Apr 15, 2014 at 2:45 AM @BillMcC wrote: + +Like the concept, don't like the name. "Strict Module" in VB implies to me the code inside this module is Option Strict On// strictly speaking that is ;) + +"Shared Class" on the other hand is what I would expect when translating "static class" from C#. It would also fit closest to the case of partial private sub new scenario. + +It would also be nice if we could turn OFF all auto importing. For example, let's say Console is a standard module and gets auto-opened. In a VB project with +Imports Microsoft.VisualBasic +Imports System + +You'd be faced with conflict resolution over Writeline. + +It'd be nice to be able to indicate you don't want unqualified access to shared class members. (aliases is too narrow/ugly) +For VB, this would have to be surfaced as an "Option", eg Option AutoImports Off or something like that. + +--- + +On Apr 15, 2014 at 12:26 PM @KathleenDollard wrote: + +Did you mean "Shared Class" (then I'll reread because I didn't get it) +or +Did you typo and mean "Shared Module"? + +--- + +On Apr 15, 2014 at 1:08 PM @BillMcC wrote: + +Shared Class. A Module is already Shared//static + +--- + +On Apr 17, 2014 at 5:06 AM @lonewolfcj wrote: + +Definitely agree that the Partial Private Sub workaround should be retired (creativity notwithstanding)! + +I like BillMcC's Shared Class suggestion. My only question about it is based on how I've used Modules and Classes...I'll use a module when each declaration stands on its own and I don't want a constructor (even though I can add a constructor to a Module I never do - I would create a Shared Sub New in a class instead). + + +#PROPOSAL: USE "CONTAINED MODULE" + +So, for the scenario in which I want to add to a namespace but do not want the declarations lifted, I prefer something like "Contained Module" to "Strict Module." I think that using the Strict keyword conflates with the existing definition of Strict and doesn't cleanly describe what it's actually being Strict about. Conversely, "Contained" I think describes the intent more clearly - that the members are only accessible if you specify the containing module's name. + +``` +Contained Module Monkey 'basically a namespace container (scaffolding a level of the hierarchy) + Public Function x() as Integer = Year(Now) 'shameless plug for expression as return value +End Module + +Module PervasiveMonkey 'lifted for general use + Public Function y() as Integer + Return 20 + End Function +End Module + +Module Program + Public Sub New() + Console.Writeline(Monkey.x) + Console.Writeline(y) + End Sub +End Module +``` + +#IDEA: IMPLICITLY CREATE CONTAINED MODULES +_(This one might be boutique exotic goldplating heresy)_...to add to the Contained Module concept - another idea would be to allow adding declarations directly into a Namespace block and let VB create implicit Contained Modules (c# static classes) from the outermost namespace level. + +so: + +``` +Namespace Grover.Cleveland + Function AmIPresidential() as Boolean = True +End Namespace +``` + +Would create an implicit Contained Module Cleveland (static class) in the Grover namespace, as: + +``` +.namespace Grover +{ + .class private abstract auto ansi sealed beforefieldinit Cleveland + { + .method public static boolean AmIPresidential() cil managed + { + ... + } + } +} +``` + +_(Adding a Cleveland Class in the Grover Namespace would be met with an error since it's already implicitly defined as a Contained Module)._ + +#PROPOSAL: ALLOW NESTING OF CONTAINED MODULES + +Contained Modules (whether explicitly or implicitly defined) could accommodate nesting, which would be useful when laying out static hierarchies. The current alternative is to either create explicit Namespace blocks or rely on nesting Public Classes within Modules and play tricks to eliminate the constructors (which won't even be an option anymore). + +``` +Contained Module RootModule + Function Here() as String = "Quip" + + Contained Module NestedModule ' + Function InHere() as String = "Quill Pen" + End Module +End Module +``` + +_(Regular Modules cannot be nested within a Contained Module)_ + +``` +Module Program + Sub Main() + Console.Writeline(RootModule.Here) + Console.Writeline(RootModule.NestedModule.InHere) + + 'also, accessing a static from a With Block would be highly convenient: + With RootModule + Console.Writeline(.NestedModule.InHere) + End With + End Sub +End Module +``` + + +--- + +On Sep 19, 2014 at 10:44 PM @lonewolfcj wrote: + +Lucian/Anthony, + +I read the 9/10 updates to the comment and am still perplexed as to why Explicit Modules could not be nested? This would be useful, particularly when organizing hierarchies of shared methods. Nesting classes with shared methods works fine but I think a key distinction between modules and classes are that modules cannot be instantiated. So it would then be useful to have nested modules as the better option for that use case. + +Craig. + +--- + +On Sep 20, 2014 at 11:10 PM @ADGreen wrote: + +Hey Craig, + +I'm not sure to which update you refer but the general feeling in the VB LDM was that nesting should be permitted for the reasons you mention. In general the momentum was behind complete functional parity with C#'s static classes, so: +* Nesting should be permitted. +* Being generic should be permitted. +* Being partial should be permitted. + +All of those capabilities needn't be implemented at the same time though. The highest priority scenario identified was not spilling their members into the containing namespace. + +__-ADG__ + +--- + +On Sep 21, 2014 at 4:03 PM @lonewolfcj wrote: + +Anthony, + +Good to hear. I was referring to the original post which was updated on 9/10 with (what I thought was) your suggestion to create "Explicit Module". I matched that to the Semantics section which says: + +"Q. Nesting? Currently VB modules can't be nested. C# static classes can be nested. +RESOLUTION: Strict modules cannot be nested." + +So I drew the conclusion that the first cut of Explicit Modules will not allow nesting. + +Craig. + + +--- + diff --git a/meetings/2014/LDM-2014-02-17.md b/meetings/2014/LDM-2014-02-17.md new file mode 100644 index 0000000..78a6439 --- /dev/null +++ b/meetings/2014/LDM-2014-02-17.md @@ -0,0 +1,1414 @@ +VB Language Design Meeting 2014-02-17 + +ROLL-UP OF PROPOSED FEATURES. + +*Please don't reply to this post with comments on individual language features. That will get out of hand because there are so many of them. If you have comments on an individual feature, please create a new post for it. If the feature's also in C#, then cross-post to the "C# language design" topic.* + +We spent this meeting on a complete roll-up of all the Dev14 ideas that have been floated and designed so far. This isn't a commitment that we will have time to finish designing or finish implementing them. It's just where we are at the moment. + +The notes from this meeting are deliberately "RAW". I haven't spent time to type them up neatly. My goal is to get all language notes out in the open, where the public can see them, with as little process or hindrance as possible. I figure it's better to publish early and iterate (and fix typos), rather than publish late. If you see anything that seems wrong or you disagree with, please first seek clarification rather than taking these notes as Microsoft's definitive statement on anything. + +You might notice that I've written the date in ISO format, "2014-02-17". This is in honor of a new VB language feature that has already been implemented, "year-first date literals". + +# 1. Improved XML doc-comments. +*Approved. Already in Main. Parity with C#.* +These are now supported to the same extent as C#, e.g. crefs and paramrefs are parsed correctly by the language. (The IDE team also added a much richer IDE experience will full syntax colorization, quick-info and rename support. +![Image](http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-06-codeplex/5353.xml.png) + +# 2. Comments after implicit line-contuations. +*Approved. Already in Main. VB-specific.* +Comments are allowed after implicit line-continuation characters. Here are examples which failed to compile before, but now work: + +``` vb +Dim invites = {"Jim", ' got to invite him! + "Marcy", ' Jim's wife + "Jones"} + +Dim addrs = From i In invites ' go through list + Let addr = Lookup(i) ' look it up + Select i,addr +``` + +# 3. #Region anywhere +* Approved. Already in Main. Parity with C#.* + +"#Region" is allowed within method bodies and can cross method bodies. + +``` vb +Function Range(min%, max%) As IEnumerable(Of Integer) + If min > max Then Throw New ArgumentException +#Region "validation" + Return Helper(min, max) +End Function + +Private Iterator Function Helper(min%, max%) As IEnumerable(Of Integer) +#End Region + For i = min To max + Yield i + Next +End Function +``` + +# 4. Implement readonly props with readwrite props. +* Approved. Already in Main. Parity with C#.* + +The following code used to give an error saying that C.p doesn't match I.p and hence can't implement it. But now it's okay. (Also, you can implement writeonly props with readonly props) + +``` vb + Interface I + ReadOnly Property p As Integer + End Interface + + Class C : Implements I + Public Property p As Integer Implements I.p + Get + End Get + Set(value As Integer) + End Set + End Property + End Class +``` + +# 5. Overloads Overrides +* Approved. Already in Main. Interop with C#.* + +Previously, VB libraries had to write both modifiers "Overrides Overloads" to play nice with C# users. Now, "Overrides" members are also implicitly Overloads. + + +# 6. Allow CObj in attributes +* Approved. Already in Main. Parity with C#.* + +The following code used to give an error that CObj(...) wasn't a constant. But it is, so we no longer give the error. +``` vb + +``` + + +# 7. TypeOf IsNot +* Approved. Already in Main. VB-specific.* + +You used to have to write it the long way: +``` vb + If Not TypeOf sender Is Button Then +``` +Now you can write it more readably: +``` vb + If TypeOf sender IsNot Button Then +``` + +You could already use the "IsNot" operator previously, but only for reference comparisons e.g. "If sender IsNot Nothing Then"... + + +# 8. Readonly autoprops +* Approved, but design-questions remain. This has been prototyped. Aligns with C# vNext "getter-only and initialized autoprops" feature.* + +We all know that we want this, but we don't know exactly what we want... Here’s an example: +``` vb + ReadOnly Property p As Integer = 15 + ReadOnly Property q As Integer + + Sub New(q As Integer) + Me.q = q + End Sub +``` + +It’s clear that “ReadOnly” means that the property itself only has a getter. That’s the only thing we’re agreed upon at the moment… + +Outstanding questions: +1. Should the backing fields be visible in the designer, or hidden like they are now? +2. Is the backing field ReadOnly like C#, or mutable like a current autoprop? +3. Can you assign to a ReadOnly autoprop in the constructor (implicitly an assignment to the backing field) just like you can assign to a ReadOnly field in the constructor? + +Note1: The CLR allows an assignment-to-readonly instruction at any time, but it has no effect after the constructor has finished. The CLR allows assignment-to-readonly by reflection at any time. + +Note2: Why would you want to assign to a ReadOnly in the constructor rather than just in the property’s initializer? … Maybe it’s because you need some control-flow logic to figure out what to assign, e.g. a For loop. Maybe it’s because you assign based on arguments that were passed to the constructor – although this latter case is addressed by primary constructors. + +Here is some previous public feedback on the topic: http://blogs.msdn.com/b/lucian/archive/2010/01/30/core9-readonly-auto-properties.aspx + +Here are three mutually incompatible visions of the feature: + +PROPOSAL “A”: ReadOnly autoprop is like ReadOnly field. We are minimizing the code you need to type to get readonly properties. +Backing field is hidden from intellisense, the backing field is ReadOnly, and you can assign to a ReadOnly autoprop in the constructor. (This is like C#’s current proposal, except C# proposal doesn’t allow assignment in the constructor). + +PROPOSAL “B”: ReadOnly autoprop is like existing autoprop but without setter. We are sticking closely to the status quo, not doing anything special. +Backing field is hidden from intellisense, the backing field is ReadWrite, and you can’t assign to the autoprop in the constructor. (Note: this proposal is pretty weak unless there are primary constructors. Also, the existing VB snippets for readonly properties use mutable backing fields). + +PROPOSAL “C”: Backing fields should become first-class and well-known. They’re VB’s answer to “{get; private set}” from C#. +Backing field is shown in intellisense, the backing field is ReadWrite, and you can’t assign to the autoprop in the constructor; but you can assign to the backing field. + + +MVP feedback: Bill McCarthy, Jan Zaruba, Joacim Andersson, Klaus Loffelman all voted in favor of proposal “C”. No one voiced any other preferences. We have implemented proposal "C" in the preview, and are open to further feedback. + + +# 9. International date literals +* Approved. Already in prototype. VB-specific.* + +Design is merely to offer ISO syntax for the range of things that are covered by existing date literals for DateTime (so: no timezone, no milliseconds, no DateTimeOffset). The format for dates is YYYY-MM-DD, and they must have the full number of digits. +``` vb + Dim d = #2013-06-12# + Dim d = #2013-06-12 15:17:23# +``` + +Q0. What about the horrible hacks that Anthony discovered, about how the IDE compiler allows two-digit dates (but command-line doesn't), and … ??? + +Q1. Do you allow #2014-02-04 24:00# as well as #2014-02-05 00:00# ? +A1. The current DateTime constructor does not allow you to pass “24” for the hours. And the current VB literal doesn’t allow 24:00 as a time. So we should disallow this. + +Q2. Do these always produce a datetime with .Kind = DateTimeKind.Unspecified? +A2. Yes, since current VB DateTime constants always do. + +Q3. Do we intend to come up with a solution for declaring an optional parameter with value “DateTime.MaxValue”? Currently you can’t, because “DateTime.MaxValue” isn’t a constant, and there’s no way to write DateTime.MaxValue in the VB datetime literal format. +A3. No, no solution. + +Q4. Can we have the existing DateTime separators, or merely the ISO separator (-) ? + +Q5. Do we allow AM/PM or do we require 24 hour clock? + + +PROPOSAL “A”. This feature is just "existing DateTime literals but with four-digit year first" +Q4 = allow existing separators, Q5 = allow AM/PM + +PROPOSAL “B”: This feature is about unambiguous universal (hyphen, year-first) notation for the date which was the part that SUFFERED, but just use existing time syntax for the rest. (votes in favor: Aleksey, Klaus, Lucian) +Q4 = require hyphen; Q5 = allow AM/PM + +PROPOSAL “C”: This feature is "subset of ISO syntax for those things encompassed by current DateTime literals. (votes in favor: Neal, Dustin, Anthony) Everyone knows ISO syntax. They won’t need to stop to wonder whether #2010/10/11# means November 10th or October 11th (although there are no cultures in the world where it means November 10th). +Q4 = require hyphen, Q5 = disallow AM/PM + +We could also accept AM/PM, but (1) the prettylister prints into ISO format, and (2) we give a warning. + +RESOLUTION: proposal "A", based on discussion from 2014-02-04. Do you see what I did there? :) + +# 10. Digit group separators +*Approved, but needs further language design decisions. Already in preview. Aligns with C# vNext feature "digit grouping" + +We settled on spaces as separators per ISO recommendations, but C# preferred underscores (like Java), so we should revisit that difference. +``` vb + Dim x = &B1110 0001 1010 1111 + Dim x = &B1110_0001_1010_1111_SB +``` + +Design: this is a token-level thing. You can’t have line-continuations (implicit or explicit). For spaces, we’d only allow a single space. Not sure what we’d do with underscores. You can put the separator anywhere you want, including after &H and &O and &B, except after the &. + +Current experimental prototype allows space, underscore and backtick. Note: old calculators in the 1970s used backticks! No one likes the backtick (apart from Klaus). + +PROPOSAL 1a: the only separator allowed is underscore. + +PROPOSAL 1b: allow both space and underscore. + +PROPOSAL 2: separator is allowed before the type suffix + +PROPOSAL 3: not allowed more than one underscore in a row + +Open question: should you be allowed a separator before the numeric-type-suffix? E.g. + Dim x = &B 1110 S + + +# 11. Binary literals +*Approved. Already in preview. Aligns with C# vNext feature "binary literals" * + +It will be nice for enum literals. F# has them. We would ask the debugger to consider displaying stuff in binary as well, but that’s lower priority. +``` vb + Dim x = &B11100101 +``` + +Note: Integer.TryParse is a language-neutral .NET Framework function, that should not be augmented just for sake of new language-syntax. So we wouldn't want to make it work with the above syntax. + +# 12. Multiline strings +*Approved. Already in preview. Parity with C#. * + +``` vb + Dim x = "Hello + World" +``` +We will use the normal quotes. We are happy that the pretty-lister inserts them in pairs in Roslyn (so the remainder of the file doesn't blink on and off too much). + +Note: the pretty-lister should not auto-indent the next line when you press ENTER. (C# doesn't either) + +Q. Does a newline emit CRLF? +A. It's whatever is in the source file. That's how C# works. + +We allow any characters up until an unpaired " (or all the other string-starting-and-closing quote characters are currently allowed). + +Q. Do we allow multiline string literals in preprocessor directives? +A. No. Neither Roslyn nor Native allow implicit line continuations in preprocessor directives. (although they sadly allow explicit line continuations). + + +# 12. Select Case Typeof +*Approved, but needs further language design decisions. Already in preview. VB-specific.* + +Anthony's basic idea, which is in the preview: +``` vb +Select Case sender + Case 3 : ... + Case < 4 : ... + Case As UIElement : ... ' now works + Case b As Button : ... ' now works + Case t As TextBlock: ... ' now works + Case As UIElement, As Integer : ... ' okay in Design1 + Case 3, As String : ... ' okay in Design1 + Case ex As Exception When ex.i<7 : ... ' not supported + Case c As Control, d As DropDown : ... ' error. + Case AsNot Integer: ... ' not supported +End Case +``` + +Questions arising from the prototyping effort: +* Value types +* When clauses - Should they apply to each clause or each Case? Should they be generalized to any kind of Case clause? Should they be removed? Should we add a Continue Select as an alternative? + +**Design1**: "As String" is just a normal case, like the other cases in VB, and indeed you can combine them and jump into them. "x As String" is different: it must be the only condition on the Case line, and doesn't admit inbound jumps (similar to how For blocks don't admit inbound jumps). The scope of the variable declaration is just that case clause, and follows the same principles as other blocks which define variables like ForEach. +``` vb + Case 3, As String : ... + Case b As Button : ... +``` + +**Design2**: both "As String and "x As String cannot be comma-combined with other case clauses, and neither admits inbound jumps. The motive is to safeguard a possible future use of comma in pattern-matching. (Unfortunate, since VB doesn't have "case fall-through", and so likes to use comma to combine cases). + +**Concern**: It feels like generics will be important. How to implement them though? Answer: this is impossible in the current CLR without reflection, and we wouldn’t want a language feature that depended on reflection. Therefore this scenario is out of consideration. +``` vb + Case As IEnumerable(Of T) : fred(Of T)(Nothing) +``` + +Concern: We have to make sure that anything we support now will be compatible with a hypothetical future that allows richer pattern-matching. So let's design that hypothetical future right now even though we won't implement it. Pattern-matching is odd in a language without algebraic datatypes, because there's no inbuilt canonical way to decompose an object. Possibilities are (1) only do decomposition on types with primary constructors; (2) decompose based on property names; (3) like Scala a type has a canonical "Unapply" method which says how to decompose; it might be an extension method and might be overloaded by type or arity; (4) like F# have active patterns. + +In our investigation of pattern-matching, we're heavily swayed by Martin Odersky's paper http://www.scala-lang.org/node/112 + + +Anthony's advanced idea: +``` vb +Dim x = sender Matches Dim ui As UIElement When ui.Tag = "a" + +Select Case sender + Case Matches Dim b As Button : ... +End Select +``` + +``` +Expr ::= … | expr Matches M [when boolean-expr] +M ::= * | Dim v | Dim v As T | MatchFunction(M, …) +``` +Scope: the scope of each introduced "v" is the containing statement. So, it works in "If" and "Case", but it doesn't help with "Dim x = e Matches Dim v" + +OPTION1: can we allow arbitrary Boolean expressions inside M? Still can't see it. + +OPTION2: can we remove "Dim", and make the parentheses on MatchFunction compulsory? i.e. +``` +expr ::= … | e match M1 +M1 ::= M2 [where Boolean-expr] +M2 ::= * | Dim v | [Dim] v As T | MatchFunction(M1, …) +``` +OPTION3: Should we also we also allow `M2 ::= … | v As MatchFunction(M1, …)` + + +## TO RECAPTURE the basic question we're trying to address... +How difficult is it to match Tree(Leaf(2), Leaf(3)) + +## OPEN QUESTIONS: + +(1) How exactly will MatchFunction bind? It would be nice to have a "matcher" object that we can dispatch on. It would be nice to dispatch on Integer.TryParse. Or maybe each active pattern should be explicitly indicated with an attribute (like extension methods). + +(2) The whole idea here is to match against the runtime type of expr. How does that even work when invoking MatchFunctions? Overload resolution? It suggests putting overload resolution into the late-binder, which we don't like. + + + +# 14. Await in Catch and Finally blocks +*Approved. Aligns with C# vNext feature* + +We'll leave the design work to C# LDM, and do exactly the same. + + +# 15. Type inference for constructors of generic types. +*Approved. Aligns with C# vNext feature* + +``` vb + Dim x As New List({1,2,3}) + Dim x = New List({1,2,3}) +``` +We'll use just the same semantics as C#. + +Note: it doesn't help common cases that I run into, e.g. +``` vb +Function f() As Tuple(Of Integer, String) + If condition Then Return New Tuple(Nothing, Nothing) + Return New Tuple(5, "hello") +End Function +``` + +But it's better than nothing, and brings more regularity into the language. + + +# 16. on autoprops +*Approved. Aligns with C# vNext feature "field target on autoprops" * +``` vb + property x As Integer + event e As Action +``` +Stylistic guidance will be to use on events even though the field target isn't needed. No further semantic design work needed. + + +# 17. Allow scalar query expressions to clash names. +*Approved. Parity with C#* + +There are two parts of the approved design.... +``` vb +Dim args As String() +Dim q = From arg In args Select arg.ToString() +' BC36606: Range variable name cannot match the name of a member of the 'Object' class +``` + +Design1: Improve error message so it says “Inferred range variable name ‘ToString’ cannot conflict with blahblahblah”. + +Design2: If the last Select is followed only by the innocuous clauses (Distinct, Skip, Take) and if the Select has only one item, then skip name generation entirely. + +# 18. Introduce "Null" literal +* Rejected on 2014-01-06. It would have had parity with C# "inference is aware of null" feature. * + +Here is the problem statement: + +*“I don’t know what Nothing means.”* I wrote the following code, expecting that Nothing would mean a nullable integer, but it came back as the integer 0. +``` vb +10: Dim x = If(False, 0, Nothing) +``` + +*“Nothing doesn’t mean what I think it means.”* I wrote the following code, expecting that by specifying the type, it would know to use a nullable nothing. But it didn’t. +``` vb +20: Dim x As Integer? = If(True, 0, Nothing) +``` + +*“No one else knows what Nothing means.”* MSDN articles always say “Use null (Nothing in VB),” but that’s not really correct. Nothing is more like default(T), and VB lacks null. + + +## PROPOSAL “B”: INTRODUCE NULL INTO THE LANGUAGE +The basic problem is that Nothing was a bad primitive from the get-go, and C#’s “null” was a better primitive. We should encourage people, as best-practice, to use “null”. It would work as follows. + +Syntax: if we are parsing an expression and encounter the simple name “Null” and it doesn’t bind to any namespace, type or variable, and doesn’t have qualification or indexing or dictionary-lookup operators after it, then treat it as “Null”. + +Semantics: Null is identical to Nothing, except for the following rules: + +1. It is an error to interpret Null in a context expecting a non-nullable value type. So, “Dim x As Integer = Null” is an error, but “Dim x As Integer? = Null” is okay. + +2. Even though Null has no inherent type of its own (i.e. Null doesn’t suggest its own type to dominant-type algorithm), nevertheless "Dim x = Null" infers type Object for x. This is similar to the existing rule that "Dim x = Nothing" infers Object even with Option Strict On. + +3. If the compiler needs to determine dominant-type-of-set-of-expressions, and one of those expressions is a naked Null literal, then when gathering the set of inherent types of all those expressions, replace each non-nullable-value-type candidate “T” with Nullable(Of T). + +Example: "Dim x = If(b,5,Null)" previously gathered only one inherent type "Integer" as a candidate for dominant-type-inference. From rule [3] it will instead gather “Integer?” as a candidate. Both expressions can be interpreted as Integer?, so that succeeds. + +Example: “Dim x = {1,2,Null}” previously gathered only one inherent type “Integer” as a candidate for dominant-type-inference. From rule [3] it will instead gather Integer? as a candidate, and it works. + +Example: “Dim x = {1, “hello”, Null}” previously gathered Integer and String as candidates. From rule [3] it will instead gather Integer?, String as candidates. There is no dominant type amongst these two, so it falls back to Warning: Object Inferred. + +Example: “Dim x = {1L, 2, Null}” previously gathered Integer and Long. From rule [3] it will gather Integer? and Long?. The dominant type amongst these two is Long?, which is what it will pick. + +Incidentally, the VB compiler up to VS2013 already recognizes syntactic “null” in the same way, in order to produce a special-case error: +![Image](http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-06-codeplex/5481.null.png) + +We did consider “Nothing?” instead of “Null”. That has the slight advantage that Nothing is already a reserved word in VB. However, it looks strange, and will leave people even more confused. We also considered “Nuffink” :) and rejected it -- it’s not all dour-faced diligence in the VB LDM! + + +# 19. Smarter name resolution +* Approved. Already in Main. VB-specific. * + +Full notes are recorded in the minutes of the VB LDM 2013-12-13. I haven't typed them up onto codeplex yet. If you want me to, please request them. + +``` vb +Import NA ' contains only the type NA.N1.T +Import NB ' contains only the type NB.N1.U +Dim x As N1.T ' let this resolve to NA.N1.T + +Dim x As ComponentModel.INotifyPropertyChanged +' In Console is okay, but in Winforms apps it is ambiguous +' between System.ComponentModel and System.Windows.Forms.ComponentModel +' Note: System.ComponentModel isn’t project-imported on most project types +' so the full qualification might be expected. + +Threading.Thread.Sleep(1000) +' In Console and Winforms this is okay, but in WPF aps it is ambiguous +' between System.Threading and System.Windows.Threading +' Note: System.Threading isn’t project-imported on most project types +' so the full qualification might be expected. + +Dim y As Xml.Linq.Xdocument +' If you import System.Security.Cryptography, this becomes ambiguous +' between System.Xml and System.Security.Cryptography.Xml +' Note: System.Xml isn’t project-imported on phone/silverlight, +' so the full qualification might be expected. + +Diagnostics.Debug.WriteLine() +' In Silverlight/Phone this works, but in WinRT aps it is ambiguous +' between System.Diagnostics and Windows.Foundation.Diagnostics +' Note: System.Diagnostics isn’t project-imported on phone/silverlight, +' so the full qualification might be expected. +``` + +# 20. Partial modules and interfaces +* Approved. Aligns with C# vNext feature "allow partial anywhere" * +``` vb + Partial Module Module1 + End Module +``` +Partial methods inside will be allowed; they will just drop out. We can't think of any design gotchas. This was a request from the Roslyn implementors. + + +# 21. Params IEnumerable +* Approved. Aligns with C# vNext feature. * + +``` vb + Sub f(ParamArray x As IEnumerable(Of Integer)) +``` +No further design work needed. + + +# 22. Private Protected. +* Approved, but syntax needs to be settled. Aligns with C# vNext feature.* + +Design option 1: like C# +``` vb + Private Protected Sub f() + + Friend Property p As T + Get + Private Protected Set + End Property +``` + +Design option 2: not like C#, but it is conceptually cleaner. VB should naturally be more verbose where verbosity is actively useful. +``` vb + Protected And Friend Sub f() + + Friend Property p As T + Get + Protected Set + ' means anyone in the assembly can see it, + ' but only those who also derive can set it + End Property + + Protected Property p As T + Get + Friend Set + ' means it's a protected property, but + ' only my assembly can set it + End Property +``` + +RESOLUTION: Please see meeting notes from 2014-03, where we opted for "Design 3". (I haven't typed up those notes yet. If you'd like to see them, let me know -- lwischik). + + +# 23. TryCast for nullable value types +* Approved. Parity with C#.* + +TryCast will allow a target type that's either Reference type or a nullable type. It will work exactly as in C#. Note that TryCast bypasses the latebinder. +``` vb + Dim obj As Object = 1 + Dim x = TryCast(obj,Integer?) +``` + +# 24. Override events +* Approved in principle, but still needs design work. This is a parity issue for BCL and other frameworks* + +Note: it is CLS-compliant for the BCL to do this. The only CLS restrictions around “abstract” are with generics. + +Currently C# lets you +1. Define an interface with an event. +2. Implement the interface by providing that event +3. Define an abstract class with a MustOverride event +4. Inherit from that class and override the event +5. Define a class with an Overridable event +6. (there’s a C# bug when you try to inherit from that class and override the event, but there are workarounds) + +VB lets you do (1) and (2). This means that VB users are shut out from any library which uses (3) or (5): VB users are unable to inherit from the class. +In .NET4, the BCL folk have changed a few classes to include MustOverride events. For instance, Inherits System.Windows.Documents.Serialization.SerializerWriter. The only VB workaround is mock-type injection voodoo. (e.g. also the VS Editor folks, writing in C#, had done this themselves). + +VB's lacuna was just an oversight. + +Proposal at hand is (3) let you define an abstract class with MustOverride event, (4) let you inherit from that class and override the event, (5) let you define a class with an overridable event. + + +# 25. Allow MyBase in structures +* Approved, but low priority. VB-specific. * + +The example is MyBase.GetHashCode(). It should behave like you'd expect, like C# does. + +NB. Structure "GetHashCode" finds the first instance field and calls GetHashCode on it, using reflection  + + +# 26. Allow delegates in object initializers +* Approved in principle if C# goes ahead with the same feature, but needs further work on syntax* + +``` vb +Dim x As New C With {.Name = "x", .Notify += delegate} +Dim x As New C With {.Name = "x", AddHandler .Notify, delegate } +Dim x As New C With {.Name = "x", .Notify = delegate } +Dim x As New C With {.Name = "x", .Notify := delegate } +``` + +We're not sure what syntax to use… ++= is the C# syntax and resonates with our proposal to add += for delegates, even though the feature only allows you to sign up a single delegate += is the XAML syntax, and nice and short. If the constructor of C had already signed up a delegate, it'd be weird! It's also nice not to add new syntax. +:= is the named-argument syntax + +NOTE: maybe there are problems to be solved at the same time for nested collection initializers, and of course lightweight-dynamic initialization. C# lets you use assignment for collection-initializers: +var x = new Order { .Items = {1,2,3} } +This gets the value of "Items" and then does a collection-initializer on it. We can't do this. But it shows there exists in C# a precedent where = really means Add. + + +# 29. Allow With and From together +* Approved, but low priority. VB-specific. * + +This is just cleaning up the language. Design is that "With" comes first and "From" comes next. +``` vb + Dim x As New List(Of Int) With {.Capacity=10} From {1,2} +``` + +# 30. Extern alias for assemblies +* Undecided.* + +We have a design doc for this, but it's a lot of work, and seems low-priority. + +# 31. Combine delegates with += +* Approved in principle, but need design work. Parity with C#. * + +``` vb + m_ClickEventHandler += value + m_ClickEventHandler -= handler +``` +Semantics will be exactly as for C#. However we will not also allow "event += value" as a synonym for "AddHandler event, value". + +STILL TO DO: figure out exact semantics for "+" +``` vb +m_ClickEventHandler = m_CLickEventHandler + value +``` +STILL TO DO: figure out (for this as for all new features) how much should make it into the late-binder? + + +# 32. String interpolation +* Undecided * + +``` vb + Dim x = $"hello \{e.Name}" +``` + +We have detailed notes from this from VB LDM 2013-12-09. It's a complicated topic. If you'd like me to type up the notes, let me know. -- lwischik + + + +# 42. Out parameters and implicit Out parameter declaration +* Approved in principle, but needs design work. Aligns with C# "out vars" feature.* + +``` vb + ' Callsite: + If Integer.TryParse(s, Out [Dim] x [As Integer]) Then ... + + ' Declaration-site: + Function TryParse(s As String, Out x As Integer) As Boolean +``` + +The declaration-site will be emitted as Byref. However if you merely write Byref then it will be treated as a ByRef rather than as an Out, because the compiler doesn't generally crack attributes in source code. + +The declaration method will default-initialize all Out parameters on entry (this can be optimized away by the compiler). Warnings will be emitted for both use-before-assign and return-without-assign. + +On the callsite, for back-compat, you don't need an "Out/Output" keyword to match an out parameter. However, you do need the keyword in order to declare the variable inline, and if you use the keyword then it is an error to pass it something that's not an lvalue. + +Outstanding questions: + +* What should the scope of "x" be? C# LDM is also wrestling with this, and the answers are sometimes unexpected. + +* Should the pretty-lister insert "Out" as you type a function invocation? + +* Callsite syntax: should it be "Out" or "Output"? Should we use rules similar to the existing "For" where you don't need Dim, and instead a new variable is implicitly declared if there's an "As" clause or if it doesn't bind an existing name? + +* Is this synergy with "For" a step too far? Given that the scope of the out in statements will bleed outside. Shouldn't we help people avoid the dangerous situation? + + +# 34. Declaration expressions +* Rejected. This would have had parity with C# vNext feature. * + +This is a generalization of Out Dim, to allow variables to be declared in any expression. + +``` vb +Console.WriteLine(Dim x As Integer = GetValue(), x + 2) +Console.WriteLine(x+1) + +If (Dim c = TryCast(x, String)) IsNot Nothing Then ... +var z = (var x=LengthyGetX(); var y = LengthyGetY(); x*x+y*y); + +If (Let c = TryCast(x, String) in c) IsNot Nothing Then + +var z = Let x = LengthGetX() + Let y = LengthyGetY() + Returns x*x + y*y + +If (Let c = TryCast(x,String)) IsNot Nothing + +var z = Let x = LengthGetX() + Let y = x+LengthyGetY() + Select x*x + y*y ' DO NOT want x to escape scope +``` + +"Select"? Yield? Returns? In? + +``` vb +var z = Let x = LengthGetX(), + y = x+LengthyGetY() + Yield x*x + y*y ' DO NOT want x to escape scope + +Integer.TryParse("15", Out x) +Console.WriteLine(x) ' REALLY DO WANT x to escape scope +``` + +C# is using the same mechanism for both "out vars" and the "let" construct. Maybe that's why C# scope isn't always obvious? + +Q. Does the keyword "Let" cause ambiguities if we're using this inside query expressions? Note that "Let" is bad for query expressions. + +Note: one thing that makes sense for C# is that C# already allowed assignment into expressions, and so assigning to a new-variable is a sensible extension. But VB has never allowed assignment in expressions, so it doesn't naturally go that far. + +RESOLUTION: None of this feels naturally "VB"ish. We're happy if VB sticks merely to Out parameters and implicit declaration of Out arguments. + + +# 35. Expression sequences +* Rejected. Would have had parity with C# vNext feature. * + +The C# goal is to allow multiple expressions. "An expression can be of the form (expr1; expr2; expr3) which has the end result "expr3" but it evaluates (and discards) the preceding expressions in the list." + +VB proposal: +``` vb +Dim x = (Console.WriteLine("log") : 15) +Dim x = Do Console.WriteLine("log") In 15 +``` +an expression can have the form "Do statementlist In expr" which has the end result "expr" but it performs the preceding statementlist first. + +``` vb +Dim z = Do Dim x = sideeffect() : Dim y = sideeffect() In x+y + +Dim z = Do + Dim x = sideeffects() + In 15 + +Dim x = Function() + Console.WriteLine("log") : Return 15 + End Function.Invoke() +``` + +RESOLUTION: If we don't have declaration expressions, then there's little value in expression-sequences. It might make sense to combine the two into a "Let" operator, but that doesn't feel very VBish. + +# 37. Implicit Interfaces +* Tentatively approved, but still needs design work. Parity with C#. * + +One typical scenario for it is code-generators and partial classes. + +``` vb +Class C : implements IDisposable + Sub Dispose() : End Sub +End Class +``` + +Proposal is to allow you to avoid use of "Implements" keyword. Note that C# also lets inherited members implement interface members. We might not do the same. + +But would there be breaking changes? … yes if the base case implements stuff. Here's the example: + +``` vb +Class A + Sub Dispose() : End Sub +End Class + +Class B : Implements IDisposable + Sub Dispose() Implements IDisposable.Dispose + End Sub +End Class + +Class C : Inherits B : Implements IDisposable + ' Q. does C implement IDisposable.Dispose over again, or rely on base class? + ' Currently works fine, relying on B's implementation of IDisposable + Sub Dispose() : End Sub +End Class + +Class D : Inherits A : Implements IDisposable + ' Q. Does C implement IDisposable.Dispose via the base class, or not? + ' Currently an error +End Class + +Class E : Inherits B : Implements IDisposable + Sub Dispose() : End Sub + ' Q. does E implement IDisposable.Dispose via its method, or rely on base class? + ' Currently works fine, but it does NOT use E's "dispose" to implement the interface +End Class +``` + +Proposal1: If existing VB fails to come up with a method that implements the interface member, THEN we look for an implicit implementation of it. (Worry: if someone modifies the base class to now implement the interface method, then the derived class will no longer be implicitly implementing it. But note that it's already the case that base-class-authors can do a whole load of things that mess up your code.) If something gets hijacked, then give a warning. + +Problem: principle is that "adding interface-implementation to a base case should not be a breaking change". The above proposal would violate this principle. (Actually, the principle isn't fully upheld already. Consider a class "X" +``` vb +Sub f(dummy As Y) ' X has a narrowing conversion to Y +Sub f(dummy As I) +``` +In the first release, it would implicitly pick the first overload. But if someone now changes X to implement I, then it would pick a different overload. So we don't believe that the principle is fully upheld. Maybe the principle is a smaller one. "If a new interface is added, and an existing class is changed to implement that interface, then it won't be a breaking change". + + +NB. Here's a "bridge-method" oddity: +``` vb +class B { public void Dispose(); } +class C : B, IDisposable {} +// in C#, the CLR likes interface implementations to be virtual, so C# compiler creates a bridge method. +``` + +Proposal2: we'll look up implicitly by name, and always prefer the most derived type. But we'll add a warning when you would be hijacking code that already compiles. It's a warning, one that won't arise in typical case. (Note: you'd already have had to use Overloads/Shadows) +Example: +``` c# +class A : IDisposable { Dispose } +class B : A {Dispose} +Class C : B, IDisposable {} +``` +This code currently works, and does not hijack. +Proposal would change it so that C emits a warning and picks up the new one. + + +Proposal3: use new syntax, and copy the feature wholesale from C#. +``` vb +Class B + Implicitly Implements I1 + Auto Implements I2 + Implicitly I3 + Implicit I4 +End Class +``` +' As with C#, it will use explicit implements if they're present, and if absent then it will start looking in the most derived type. Hijacking will be impossible. NB. Vladimir points out that the C# implementation doesn't quite match the C# spec. + +RESOLUTION: Yes. Use Proposal3. We will look for a keyword combination that seems nice. + +Note: it's a weird situation that the more desirable syntax "Implicitly Implements" is more verbose than the less desirable traditional syntax "Implements". There may be IDE codespit ameliorations. + +NB. Like in C#, a single method can implement the same named member from multiple interfaces. (currently can with the Implements keyword). + +Q. How discoverable will it be? + +Q. Currently VB requires explicit interface implementation to have exactly the same optional parameter defaults. Should we adopt C# rules here? or VB? Note that C# came from a history that the camel's nose was already in the tent. Oh, and what if there are multiple interfaces with different default values? + +RESOLUTION: Stick with current VB rules, and if they complain, we can look into it then. And refactoring makes C# a bit dangerous. Note: we should match based on signature, and then give an error if the defaults don't match. + + +# 38. GetName operator +* Tentatively approved, but there are design issues. Parity with C# vNext "nameof" feature. * + +Here are some sketch syntaxes we played around with... + +``` vb +Dim name = nameof(Point.X) +Dim name = GetName(Point.X(Of ,).Y) ' we prefer GetName + +DIm name = nameof(System.Action) + +Dim TextProperty As DependencyProperty = DependencyProperty.Register(GetName(Text), GetType(String), GetType(MyClass)) + +Public Property Text As String + Get + Return CStr(GetValue(TextProperty)) + End Get + Set + SetValue(TextProperty) + End Set +End Property + +Sub f(x As String) + If x Is Nothing Then Throw New ArgumentException(GetName(x)) +End Sub +``` + +The C# proposal is straightforward: the argument to nameof is an (optional type-or-namespace-followed-by-dot) and then an identifier. The result is the string of that identifier. + +In VB we prefer "GetName" to "NameOf". + + +Q. Will it return the casing as you passed it to GetName, or will it return the casing as defined in metadata? What if there are two different casings in metadata? What if there are two different casings in metadata and they disagree with the argument to GetName? - Answer: it's an error. You'll have to fall back to a string literal. + +Q. In VB can we refer to the constructor "New"? - No, there's never any need for it. + +Q. Can "Point" be ambiguous? What if "Point" is a namespace, and member "X" exists in multiple modules inside that namespace? + +Proposal1: "Point" (the prefix) must be unambiguous, but it's fine for "X" to refer to a method-group or a property-group or a unique thing. This is the current name lookup rules. + +Q. Should we allow unbound generic types, similar to GetType? +A. Yes. + +Q. Can we use type keywords like "Integer" and "Boolean"? (same question for C#). + +Q. If we GetName() on an alias, does it refer to the underlying metadata name? Presumably yes! + + +GENERAL FEEDBACK: Concern from Aleksey and IDE team about overloads and dealing with ambiguity. E.g. when you do find-all-references, or rename-refactor on an overloaded thing, then it's not clear which of the overloads "nameof(...)" operator is referring to (hence not clear whether it should be renamed). And the Roslyn APIs that return reference-counts -- how should they deal with it? + +TWO WAYS TO SPEC THIS: +(1) GetName( BLARGH . Identifier ) where GetType(BLARGH) is valid +(2) GetName( current name lookup rules ) and the name lookup must be successful. (but need it also be unambiguous?) + + +# 39. Extension statics + +This topic has been discussed offline. Let me (lwischik) know if you'd like me to type up the notes. + +# 40. Expression-bodied function members +* Rejected. Would have had parity with C# vNext feature.* + +In C# it'll look like this: +``` csharp +public int R => Math.Sqrt(X*X + Y*Y); // readonly autoprop +int M(int i) => i+1; // methods +static operator int(C x) => (int)x.field; // conversions +static operator +(C x,C y) => new C(x.field + y.field); +``` + +C#: all of these things are expressions, but statement-expression is a kind of expression, so they allow all statement-expressions as well (including void-returning statement expressions). + +Is there anything to do in VB? + +VB single-line lambdas: +``` vb + Dim x = Function(y) y+15 + Dim z = Sub(y) Console.WriteLine(y) + +Class C + Public Square(x As Integer) x*x +End Class +``` + +Proposal 1: allow more things on a single line... We're already part way there (Roslyn now no longer requires "End" to start a line). It just requires allowing Sub() and other things to have a colon following them. + +``` vb + Dim lambda = Sub() : Return 5 : End Sub + Sub method() : Console.WriteLine("oops") : End Sub + Property p As Integer + Get : Return _p : End Get + Set : _p = value : End Set + End Property +``` + +(Q. are there any ambiguities between multiline lambdas inside methods? Follow-up with Tomas/Anthony for more…) + + +Proposal 2: Invent a special-case syntax single-line functions, methods, operators and readonly autoprops. +``` vb + Function f() As Integer : Return 15 + Sub g() : Console.WriteLine("hello") + Sub ToDo() : Throw New NotImplementedException() + Shared Operator +(x As C, y As C) As Integer : Return x.i + y.i + ReadOnly Property p As Integer : Return 5 +``` +* Note: this prevents Proposal1, which is a bit of a shame! Unless you come up with the rule that "End Sub" is optional, like ; is in javascript. + +Q. What if I put three statements? would I still need "end sub" ? +``` vb + Sub g2() : x=15 : y=17 + Console.WriteLine(x+y) + + Function f() As Integer = 15 + Sub g() = Console.WriteLine("hello") + Shared Operator +(x As C, y As C) As Integer = x.i + y.i + ReadOnly Property p As Integer = 5 ' doesn't work + + Function f() As Integer := 15 + Sub g() := Console.WriteLine("hello") + Shared Operator +(x As C, y As C) As Integer := x.i + y.i + ReadOnly Property p As Integer := 5 + + Function f() As Integer 15 + Sub g() Console.WriteLine("hello") + Shared Operator +(x As C, y As C) As Integer x.i + y.i + ReadOnly Property p As Integer 5 + + Function f() As Integer Return 15 + Sub g() Console.WriteLine("hello") + Shared Operator +(x As C, y As C) As Integer Return x.i + y.i + ReadOnly Property p As Integer Return 5 +``` + + +Proposal 3: Only solve it for autoprops, but use a new keyword: +``` vb + Computed Property p As Integer = x +``` + +* This looks confusing! It looks more complicated than what it's actually doing. +* REJECTED proposal 3. + + +VB already has +``` vb + Property p As Integer = GetFoo() + 17 ' this is done at initialization-time +``` + + +RESOLUTION: We're proud not to do anything. None of the proposals buy that much, and none are that special. + + +# 41. Primary constructors. +* Rejected for VB. Would have had parity with C# vNext feature. * + +The notes are offline. Let me (lwischik) know if you'd like me to type them up. + + +# 43. Literal type suffixes for Byte and SByte. +* Approved.* + +``` vb +Dim x = 15L +Dim x = &HFACEL +``` + +Literals are NOT target-typed in VB. They are integers (unless with type suffix). + +Byte is the only numeric type in VB which lacks a suffix. The reason it lacks it is because the obvious choice "B" is already taken by hex digit. + +Byte is unusual - it's the only numeric type where the default is "unsigned". e.g. "Integer" means "Signed Integer" but "Byte" means "Unsigned Byte". + +RESOLUTION: yes. Use "SB" and "UB". That will avoid the confusion of the previous paragraph. (other contenders were "Y and SY" like F#, or "Y and UY"). + + +Q. Can you write "Dim x As Integer = &H00000000000000000000FACE ? +Likewise can you write "Dim x = &H0FUB" + +Q. Can you have extra "1"s? e.g. Dim x As Signed Byte = &B111111111111110 SB ? The point is that leading 1s are not significant for signed binaries. + +Suspect the current rule is "any leading bits you specify had better be zero". That's a decent enough rule. We could get even more strict ("don't have more leading zeros") but that would be inconsistent. + + +This feature was triggered by a request of MVP Bill McCarthy, who wrote: + +So If I want to force a binary number to Int16, I can use the type suffix: +``` vb + Dim i As Int16 = &B 1000 0000 0000 0000S +``` + +Without the S this would fail as the literal would be an overflow for Int16. First off, I think it would be nice if the S could be spaced, eg: +``` vb + Dim i As Int16 = &B 1000 0000 0000 0000 S +``` + +And it'd be nice if there was an error correction suggestion that suggested the S suffix. + +And finally, for signed bytes, could we perhaps have a SB suffix, eg: +``` vb + Const b as SByte = &B 1000 0000 SB +``` + +The following workaround just doesn't do it for me: +``` vb + Const b As SByte = CSByte(&B 1111 1111 1000 0000S) +``` + +MVP Cory Smith wrote: +I do agree that if you specify the SByte, it should attempt to do the appropriate conversion without requiring the following: +``` vb + Dim s As SByte = CSByte(&B00010001) +``` + +Especially if this works: +``` vb + Dim b As Byte = &B00010001 +``` + +Here’s another option: +``` vb + Dim s As SByte = 1 +``` + +This works as well, so shouldn't any string literal work? I'm just guessing that this was missed somewhere along the way and is an unintended bug in the current implementation. My expectation would be that the implicit conversions for any literal would work like any other literal whether it is a number or a &B. + +# 44. Dictionary member initializers +* Under discussion. Parity with C# vNext feature. + + +Currently VB lets you write dictionary-lookup expressions +``` vb + Dim x = y!name ’ becomes this C# thing y[“name”] +``` + +C# is considering the following: +``` cs +var expr = new Exception("oops"); +var x = new JSON{ + $foo = 15, + ["bar"] = 17, + [expr] = 9 } +``` + +Proposal is to allow such things in VB object initializers. Here are possibilities... +``` vb +1: Dim x = New C With { .member = "hello" } ' existing members +2: Dim x = New C With { !name = “hello” } ' dictionaries + +3: Dim x = New C With { (expr) = "hello" } ' dictionaries +4: Dim x = New C With { !(expr) = "hello" } ' a bit goofy? only consider this if second form turns out ambiguous +5: Dim x = New C With { .(expr) = "hello" } + +6: Dim x = New C With { .Item(3) = "hello" } ' It's what you'd write if it were in a With block. + +Dim $temp = new C +$temp("name") = ... +$temp(expr) = ... +``` + +Option 4: ! means default-property-access always. We could backport ! to generally mean "default property access" everywhere, including expressions and With blocks. + +CON4: !x=3 and !(x)=3 now mean different things + +PRO: People ask for a way to indicate indexing rather than invocation. They've often asked for [] + +Option 3: we currently have a.member, a!name, a(index) so everything that comes after the "a" is valid also inside the initializer. NB. requires the thing to be an LVALUE, i.e. no xml-members, no invocation. + +CON: Can't use this in a With block on the right hand side, e.g. "With d : Dim a = .y : Dim b = (z) : End With" + +Option 5: We are omitting the default property name. + + +RESOLUTION: We like 1, 2 and 4. +RESOLUTION: 6 is also fine, for uniformity, but is lower priority. + + + +Q. Should we allow .$ as an alias for ! in VB? +A. No. Let's leave it to the intellisense to help people find it + + + +# 46. Dictionary literals +* Haven't got to this. Don't think it's worth it.* + +Currently VB has excellent support for array literals. But people want dictionary literals, and are forced to use anonymous-types with reflection. For instance, they write + +``` vb + InitializeMvc(New With { .arg1=“hello”, .arg2=“world”}) + Dim s = “hello \{name}”.Format(New With {.name=“fred”}) +``` + +The first example is a typical MVC idiom, where anonymous types are used instead of named arguments, for cases where named arguments aren’t flexible enough. The second example is similar how Python does string interpolation. Both cases require reflection to get at the members of the anonymous object, which is a pain. + +Look at how array literals work... +``` vb +Sub f(Of T)(x As IEnumerable(Of T)) +f( {1,2,3} ) ' today this picks up Integer as a hint for T +``` + +Proposal is to allow a new form of dictionary literal: +``` vb + Dim x = New With { arg1 := “hello”, arg2 := “world”} +``` + +Typical scenario is, as above, where the keys of the dictionary are strings that should be compared with the normal InvariantCulture comparison (since they’re used like parameter names rather than human-language strings). It’s not clear however what the type of the dictionary should be, and how it will work with type inference. For instance, + +``` vb + Sub f(x As Dictionary(Of String, Object)) + ... + Dim y = New With { arg1 := 1, arg2 := 2 } + f(New With { arg1 := 1, arg2 := 2 }) +``` + +We’d expect “y” to be inferred as Dictionary(Of String,Integer) or IDictionary or ReadOnlyDictionary or something like that. However, we’d expect the call to “f” to be compatible with the parameter type. + +Here are some sketched syntaxes to see if they look any good... + +``` vb +Dim x = New With { arg1 := "hello" } +Dim x = { arg1 := "hello" } +Dim x = { (1+2) := "hello" } +Dim x = { arg1 = "hello" } +With fred + Dim x = { !arg1 = "hello" } +End With + +InitializeMvc( { !arg1 := "hello", … } ) +``` + +Q. If we have dictionary literals, what would they generate? + +A. If type context implements IDictionary then use U for key and V for value, otherwise infer from dominant type of expressions - all like we do with array literals. + +Q. What is the compile-time type of the dictionary-literal expression? +A. IDictionary + +Q. What is the run-time type of it? +A. Dictionary + + +# 46. Fix the ternary If operator +* Under consideration. + +The basic problem is that when people see a ternary If operator with a type context +``` vb + Dim x As Integer? = If(True, 0, Nothing) +``` +then they expect it to mean this: +``` vb + Dim x As Integer? : If True Then x = 0 Else x = Nothing +``` +Proposal is that we should fix this for them as follows. When interpreting If(x,y,z) in a context in which the desired type is known, then interpret both y and z in the context of that type. + +Note that it will be a breaking change. We decided in LDMs in years past that this would nevertheless be good for the language. + +We must seek wider input on what people think about this. Once Roslyn goes open-source, it'll be great to open up to general public feedback. + + +# 47. More nullable support +* Approved. VB-specific. Completes the nullable story. + +``` vb +Dim y = CInt?(5) +``` + +The issue is about Intrinsic nullable conversion operators (e.g. CInt?()) + +Should we add them? If so, what does `CInt?(5)` mean? + +Proposal 1: `If( Is Nothing, Nothing, CInt()` +Proposal 2: `If(Integer.TryParse(, result), result, Nothing)?` +Proposal 3: `CType(s, Integer?)` + +RESOLUTION: Yes, proposal 3. + + +# 50. Allow As clause on Select clause range variables. +* Under consideration. VB-specific. + +Proposal: allow an “As Clause” on query Select clause range variable declarations. Maybe also on Anonymous-type member declarations too. + +For example, +``` vb +Dim x = From y As String In list + Let q As Integer = foo() + Select x As String = foo() +``` + +Currently you can specify "As" in Let clause, and it does an implicit conversion. + +PROPOSAL: allow you to specify "As" in Select when introducing a new variable ("range variable") with an initializer. + +Q. Can you use it after Aggregate? after GroupBy? + +RESOLUTION: Anthony will flesh out the other LINQ places where As would go. + + +# 51. More implicit line continuations +* Still under design. VB-specific. + +Proposal: allow implicit line-continuations in these places: +* Before “Then” in If … Then statements +* Before “Implements” and “Handles” clauses +* Others? + +Here are some examples... + +``` vb +If x +Then Return 15 + +If x +Then + Return 15 +End If + +If x AndAlso + y AndAlso + z +Then + Return 15 +End If +``` + +We generally like the implicit LC before the Then token. The prettylister would put "Then" aligned with the "If" and "EndIf". But if you omit the Then token (an omission only allowed with multiline ifs), then what? ...? Needs more investigation. + + +# 52. Allow Handles to be combined with Implements +* Not yet considered + + +# 53. Negative binary literals +* Not yet considered + +Proposal: If the type of the literal is a signed and has all bits explicitly specified we can create a negative number just like we do for hex literals today so +``` vb +&B 1000 0000 SB = -128 +``` + + +# 54. ?. operator, which does null-check before proceeding +* Approved, but needs more design-work + +We want to write +``` vb + Dim x = customer?.Name +``` +which would mean something like +``` vb + Dim x As String = If(customer Is Nothing, Nothing, customer.Name) +``` +except it would put "customer" into a temporary variable (to avoid evaluating it twice, and to avoid race conditions) + + +This is a huge topic. I will write up a separate set of notes. There is big debate about what happens if you have several things in sequence, e.g. +``` vb +Dim x = customer?.Name.Length +``` + +Proposal1: If you used ?. somewhere in the sequence, then you *MUST* continue to use it for every subsequent dot, otherwise it's a compile-time error. This boils down to ?. being a LEFT-ASSOCIATIVE operator, with everything that entails. + +Proposal2: In the above example, you are allowed to write "Name.Length", with the intended meaning that you the programmer KNOW that if customer is non-null then (because of invariants in your code) you know that customer.Name is also non-null, and you wish to avoid the runtime cost of doing a null-check on it. This boils down to ?. being a RIGHT-ASSOCIATIVE operator, with everything that entails. + + +We also in VB expect to allow `x?(y)` which does a null-check on x prior to indexing or invoking it. (C# would only `x?[y]` or `x?.Invoke()` -- it would not allow `x?()` for conditionally invoking delegates). + + +Q. Which language operators should have "null-checking" versions of them? We've already seen the null-checking member-access operator `x?.y` and the null-checking indexing-or-invoking operator `x?(y)`. Are there any others? + +We had wondered if there is ever need for a null-checking conversion operator, i.e. one that does a null-check prior to doing the conversion. Normally it's not needed, since users who write their own user-defined-conversions can roll the null-check into those. So the question is whether it's needed for intrinsic conversions. The only intrinsic conversion that might benefit from a null-check is `Integer? -> String`. This is the only intrinsic conversion which can throw a NullReferenceException. + +PROPOSAL 1: Allow null-checking conversion operator, for this one case. + +PROPOSAL 2: Change behavior of `Integer? -> String` intrinsic conversion, so it gives you an empty string if the input is null. + +RESOLUTION: Rejected proposal 1 because it's too niche. Proposal 2 is decent, but low priority. + + +--- +--- + +On Apr 4, 2014 at 5:16 PM @AdamSpeight2008 wrote: + +Re: __46. Dictionary literals__ + +Instead of a Dictionary literal, I think it could be better to implement a Key-Value Pair literal instead. (Think JSON) +A Key-Value Pair Literal ```{ "A" : 1 }``` +This re utilises the Expression / Statement Continuation Character in VB.net and Array Literals. + +In C# ```:``` is only used in _(as far I can tell)_ + +``` + cond ? trueExpr : falseExpr ' Inline IF + class B : A ' Inheritance +``` + +both of which are contextually different so I can't see another contextual definition being a problem. + +This would in essence make a Dictionary Literal just a collection of Key-Value Pairs. Eg + ```Dim myDict = { { "A" : 1 } , {"B" : 2 } , { "C" : 3 } } ``` +Is then translated into the following code. +``` +Dim myDict As New Dictionary(Of String, Integer)( { ( "A" : 1 ) , ( "B" : 2 ) , ( "C" : 3 ) } +``` + +This then only needs a overload for the constructor that takes an ```IEnumerable(Of IKeyValuePair(Of K, V))```. + +The compiler could also check at compile-time that the dictionary initialisation is valid. Eg Are the Keys Unique. + +--- + +On May 14, 2014 at 9:51 PM @AdamSpeight2008 wrote: + +Allow ```With``` on the ```Case Else``` clause +would permit the following style of coding + +```vb + Select Case flags + Case CharFlags.White : ' stay in FollowingWhite + Case CharFlags.CR : state = AccumulatorState.CR + Case CharFlags.LF : state = AccumulatorState.Done + terminatorLength = 1 + Exit While + Case Else When (flags And (CharFlags.Complex Or CharFlags.IdentOnly)) <> 0 + : state = AccumulatorState.Bad + Exit While + Case Else : state = AccumulatorState.Done + Exit While + End Select +``` + +rather than current (less elegant work around) +```vb + Select Case flags + Case CharFlags.White : ' stay in FollowingWhite + Case CharFlags.CR : state = AccumulatorState.CR + Case CharFlags.LF : state = AccumulatorState.Done + terminatorLength = 1 + Exit While + Case Else + If (flags And (CharFlags.Complex Or CharFlags.IdentOnly)) <> 0 Then + state = AccumulatorState.Bad + Exit While + End If + state = AccumulatorState.Done + Exit While + End Select +``` + +Also allow the ```When``` to the preceded with an implicit line continuation. So that if can be placed / aligned underneath the relevant ```Case``` clause. +This will improve the readability and formatting. + +```vb + Select Case baseCorTypeId + Case SpecialType.System_Enum : result = TypeKind.Enum ' Enum + Case SpecialType.System_MulticastDelegate : result = TypeKind.Delegate ' Delegate + Case SpecialType.System_Delegate + When Me.SpecialType <> SpecialType.System_MulticastDelegate : result = TypeKind.Delegate ' Delegate + Case SpecialType.System_ValueType + When Me.SpecialType <> SpecialType.System_Enum : result = TypeKind.Structure ' Struct + Case baseCorTypeId + When Me.Arity = 0 AndAlso + Me.ContainingType Is Nothing AndAlso + ContainingPEModule.Module.HasAttribute(Me.m_Handle, AttributeDescription.StandardModuleAttribute) + : result = TypeKind.Module + End Select +``` + +_Note: The code is from my roslyn fork_ + +--- + +On May 17, 2014 at 1:51 PM @AdamSpeight2008 wrote: + +The ```When``` clause should bind to each ```caseClause``` and not the entire collection of ```caseClause``` of that ```caseStatement``` + +__Composition__ +``` + Case caseClause_1 , caseClause_2 When BoolExpr : caseActionExpr + | caseClause | | caseClause | | whenClause | | | + +------------+ +------------+-+-------------+ +--------------+ + | caseClauseWithCondition | + +----------------------------+ +|----------------------------------------------------------------------| +| caseStatement | ++----------------------------------------------------------------------+ + +``` + +__Usage example__ + +```vb +Select Case x + Case Is < 0 : Throw someException + Case 0 When a_Cond, + 1 When b_Cond : Return "Result A" + Case 2 When a_Cond : Return "Result B" + Case Else When b_Cond : Return "Result C" + Case Else + Return "Other" +End Select + +End Select +``` + +would be functionally equivalent to the following ```If ... ElseIf ... Else ... End If``` construct +``` +If ( x = 0 ) Then + Throw SomeException +ElseIf ( (x = 0) AndAlso ( a_cond ) ) OrElse + ( (x = 1) AndAlso ( b_cond ) ) Then + Return "Result A" +ElseIf ( (x = 0) AndAlso ( a_cond ) ) Then + Return "Result B" +Else + If ( b_cond ) Then + Return "Result C" + Else + Return "Other" + End If +End If +``` + +--- + diff --git a/meetings/2014/LDM-2014-03-12.md b/meetings/2014/LDM-2014-03-12.md new file mode 100644 index 0000000..6730d79 --- /dev/null +++ b/meetings/2014/LDM-2014-03-12.md @@ -0,0 +1,106 @@ +# ProtectedAndFriend, ProtectedOrFriend + +## BACKGROUND + +The CLI includes two access modifiers "FamilyAndAssembly" (which allows access to derived types that are in the same assembly) and "FamilyOrAssembly" (which allows access to types if they are derived, and also allows access to types if they are in the same assembly). The concepts are explained well here: http://stackoverflow.com/questions/22856215/what-is-the-meaning-of-the-planned-private-protected-c-sharp-access-modifier + +## REQUEST +VB and C# already have syntax for FamilyOrAssembly ("Protected Friend"). Please add syntax to allow VB to express FamilyAndAssembly. + +## RESOLUTION +VB will add two new access modifiers, which are exact transliterations of the CLI keywords... + +``` vb +ProtectedAndFriend +ProtectedOrFriend +``` + +The pretty-lister will not turn "Protected Friend" into "ProtectedOrFriend". However, if the user types "private protected" (or whatever syntax C# chooses) then it will pretty-list the entire file into C#. JOKE. No it won't. But it may pretty-list the C# syntax into "ProtectedAndFriend". + +Here are some code samples: + +``` vb + ProtectedAndFriend Sub f() + + Friend Property p As T + Get + Protected Set + ' means anyone in the assembly can see it, + ' but only those who also derive can set it + End Property + + Protected Property p As T + Get + Friend Set + ' means it's a protected property, but + ' only my assembly can set it + End Property +``` + +## DISCUSSION +Alex wrote, "Around Private Protected, I really feel like we make a mistake if we choose our VB syntax with the arbitrary choice that C# made so that it could look like C++. We should embrace this as one of those times where VB’s trend towards verbosity leads naturally to a design whose meaning is obvious at first glance. We don’t want to get stuck in a design rut where we’re OK with C# being clearer than VB where terseness pays off, but not vice versa where verbosity pays off – by definition, then, VB will always be less clear than C#." + +Neal observed that the sense of "And/Or" is confusing. It might be a logical And of the permissions, or a logical And of the restrictions. Nevertheless, the CLI has chosen a canonical sense, and we're copying it. + +## OTHER DESIGNS CONSIDERED + +``` c# +private protected +protected if internal +internal && protected // also "internal || protected" +internal and protected // also "internal or protected" +internalAndProtected // also "internalOrProtected" +``` +``` vb +ProtectedAndFriend ' also "ProtectedOrFriend" +Protected And Friend ' also "Protected Or Friend" +ProtectedFriend ' rejected because it's just one space away from "Protected Friend" +FamilyAndAssembly ' also "FamilyOrAssembly" +Private Protected +Protected OnlyIn Friend +Protected But Friend +Protected If Friend +Protected When Friend +Protected Xor Friend ' just a joke... :) +``` + + +# Faster CInt(Double) + +## Background + +``` c# +double d = 10.5; +int x = (int)d; +``` +``` vb +Dim x = CInt(d) +``` + +The VB version is slower than C#. Indeed, any time we call CInt, the compiler always codegens a call to Math.Round. Even CInt(Math.Round(x)) gets compiled into Math.Round(Math.Round(x)). + +## Request +Please allow us to write fast integer casts in VB. + +## Resolution + +**Part 1**: Any time the compiler generates an implicit call to Math.Round (i.e. as part of `CInt, CLong, CULong, CUint`), and if the argument of that implicit call is one of a list of math functions known to return a whole number, then omit the outer call to Math.Round. The list of known math functions is: `Round(d), Round(d,MidpointRounding), Truncate(d), Floor(d), Ceiling(d), VisualBasic.Fix(d), VisualBasic.Int(d)`. + + +**Part 2**: If the user writes `CInt(Math.Truncate(d))` then we will emit `conv.i4` or `conv.ovf.i4` dependent on the current project-level "check-overflow" settings. (TODO: verify that `conv.i4` has exact same semantics as `CInt(Math.Truncate)`, including negative values, superlarge doubles and exceptions, NaN, Infinity). Also do likewise for `conv.i1, conv.i2, conv.i4, conv.i8` and for their unsigned versions, and for both checked and unchecked versions. + + +## Discussion + +This is a compiler optimization, pure and simple. It adds no new syntax or concepts or library functions. We identify targeted scenarios and generate optimal IL in those cases where semantics would not be affected. This seemed the cleanest approach. Specifically, it seemed better than adding any of the following syntaxes: +``` vb +DirectCast(d, Integer) +(Integer)d +VB.FastCast(d) +d As Integer +``` + +Will it be better to special-case just one of these combinations? e.g. conv.i4 on CInt(Truncate(double))? Or is it better to just do all of them? Default answer: "yes do all of them", and only scale back if the compiler-dev comes back and says it's too hard. + +There are other possible concerns, e.g. what happens if it turns out that the semantics of conv.i4 are actually different from ALL of the Math.Round-like functions? What if it turns out that it's really hard to implement every case? What if expression-trees throw a wrench in the works? Well, when devs set about implementing the feature, then we can come back to check. + diff --git a/meetings/2014/LDM-2014-04-01.md b/meetings/2014/LDM-2014-04-01.md new file mode 100644 index 0000000..7ae0e85 --- /dev/null +++ b/meetings/2014/LDM-2014-04-01.md @@ -0,0 +1,3945 @@ +# Null propagating operator ?. + +The `?.` operator is the second-highest voted request on UserVoice +http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c + +We've discussed it in C# and VB Language Design Meeting. Mads has written up detailed notes [here](https://roslyn.codeplex.com/discussions/540281) and [here](https://roslyn.codeplex.com/discussions/540514). Some questions still remain, so I'm starting a thread for general discussion of this feature. + +The core idea is to save you having to write code like this all the time, with null-checks: +``` cs +var temp = GetCustomer(); +string name = (temp == null) ? null : temp.name; +``` + +Instead you'd write this: +``` cs +var temp = GetCustomer()?.name; +``` + +## How does ?. behave in sequences? + +Q. How does it behave if you have several of these things in sequence? There are two possibilities on the table: +* *Left-associative* +* *Right-associative* + + + +``` cs +class Customer1 +{ + public readonly ExtraRef extraRef = new ExtraRef(); +} + +class ExtraRef +{ + public readonly string name = "fred"; +} + +Customer1 GetCustomer1() +{ + return (RND.Next() > 0.5) ? null : new Customer1(); +} + +int? len1a = GetCustomer1()?.extraRef?.name?.Length; +int? len1b = GetCustomer1()?.extraRef.name.Length; +``` + +What do you expect about `len1a` and `len1b`? [**edit** - initially I wrote the following out wrong. I've fixed them.] + +* len1a - this is the same for both *left-associative* and *right-associative* -- it performs a null-check on GetCustomer1(), and if that succeeds, then it will perform null-check on extraRef and name. +* len1b - under *left-associative* it is a **compile-time error**. But under right-associative, it performs a null-check on GetCustomer1(), and then **skips** the null-check on extraRef and name. + +Note: I constructed this example so that, if GetCustomer1() returns non-null, then I as the programmer know (from readonliness) that extraRef and name will both be non-null. The question is, is there some way to communicate this fact to the compiler, to avoid superfluous null-checks? **Left-associative says there isn't; right-associative says there is.** + +(I benchmarked about a 5% perf overhead of doing those extra null-checks in the case where GetCustomer1() returns non-null) + + +``` cs +class Customer2 +{ + public readonly ExtraStruct extraStruct = new ExtraStruct {age=29}; +} + +struct ExtraStruct +{ + public int age; +} + +Customer2 GetCustomer2() +{ + return (RND.Next() > 0.5) ? null : new Customer2(); +} + +int? age2a = GetCustomer2()?.extraStruct?.age; +int? age2b = GetCustomer2()?.extraStruct.age; +``` + +What do you expect about `age2a` and `age2b` ? + +* age2a (how you must write it under *left-associative*) - this performs a null-check on GetCustomer2() and if this succeeds then, thanks to **compiler-optimization** and despite what's written, it knows it can skip the null-check on extraStruct. +* age2b (how you must write it under *right-associative*) - this performs a null-check on GetCustomer2() and if this succeeds then, **because of how you wrote it**, it skips the null-check on extraStruct. + +Here, extraStruct is a structure and can never be null. So it doesn't make sense to null-check it. Should this fact be expressed as a compiler optimization in `age2a`? Or should it be expressed in syntax in `age2b`? + + +``` cs +var x = GetCustomer1()?.extraRef?.name?.Length +==> +var explainingVariable = GetCustomer1()?.extraRef; +var x = explainingVariable?.name?.Length; +``` + +* Is this a valid refactoring? + * *left-associative* : it is a valid refactoring + * *right-associative* : it's an invalid refactoring +* How would you add parentheses to make the order explicit? + * *left-associative* : `x = ((GetCustomer1()?.extraRef)?.name)?.Length` + * *right-associative* : `x = GetCustomer1()?.(extraRef?.(name?.Length))` + +You can see why we call them left-associative vs right-associative. It has to do with how you'd put parentheses into the expressions if you were writing them out explicitly. + +Incidentally, there are other places where you can't just introduce an explaining variable. For instance, in `var y = a * b + c * d`, you can't just introduce an explaining variable for the first three parts `var ev = a * b + c; var y = ev * d;`. In this case though it's because of precedence, not because of associativity. + +[NOTE: that + is meant to be a PLUS sign. I don't know what's up with the markdown...] + +Eric Lippert wrote a great blog on the associativity of the ?? operator: http://blog.coverity.com/2013/10/23/null-coalescing-bugs/#.Uzurzo1OV3Q + + +## Some other idioms with ?. + +``` cs +// Call Dispose only if x is non-null: +x?.Dispose(); + + +// Trigger an event only if it's non-null +// (not needed in VB, since this is already built into RaiseEvent statement) +event EventHandler OnFired; +void Fire() +{ + OnFired?.Invoke(this, new EventArgs()); +} +``` + + +## Codegen for ?. + +I asked some CLR experts about what assembly code would be generated by the JIT for the ?. operator, and filled in some details from my hazy memory of OS architecture... + +The question was, where does NullReferenceException currently get generated? When you do `GetCustomer1().extraRef`, and GetCustomer1() happens to return null, and so it fires a NullReferenceException, does that happen because the JIT implicitly generates a null-check? + +No. The answer is that the NullReferenceException is generated at a lower level. More specifically, in between CPU and RAM there’s a chip called "Memory Management Unit (MMU) / Page Table". Every single time a thread accesses an address in the process’s virtual address-space, the MMU looks up the address in that page table to find where the address is in physical memory. If the virtual address isn’t yet mapped to a page of memory, then the MMU generates a page-fault which the OS has to handle, e.g. by paging in from disk. The CLR reserves addresses at the bottom of virtual address space but never actually pages them in. If ever an instruction tries to look up any address there, then the MMU generates a fault, and the CLR responds to it by generating a NullReferenceException. + +The codegen for `x?.y` would probably be something like this... +``` +mov rax, X +test rax, rax +beq :lbl +mov rax, [rax + k] +:lbl +``` + +[Note: that + is meant to be a PLUS sign. I don't know what's up with the markdown...] + +--- +--- + +On Apr 3, 2014 at 8:19 PM @pminaev wrote: + +I would also suggest to add a generic monadic bind for nulls - i.e. an operator which evaluates the left operand, and if it is _not_ null, then evaluates and returns the right operand, otherwise returns null (so kinda a reverse of ??). This would be handy for covering all the various cases that are not covered by the shorthand ?. syntax, such as passing the value as a parameter to a method. For example, assuming that operator is `?:`: +``` +return s ?: Trim(s); +``` + + +--- + +On Apr 3, 2014 at 8:36 PM @ScottKay wrote: + +When passing a value as a parameter to a method, wouldn't you want to let the method decide how to best handle the null argument? If the method doesn't handle the null argument the same way as the caller would prefer, the current syntax is explicit and isn't really much of a nuisance. + +``` +return s ? Trim(s) : null; // Doesn't seem so bad +``` + +--- + +On Apr 3, 2014 at 8:57 PM @Timwi wrote: + +Surely the question is simply whether + +``` +a?.b.c +``` + +should mean + +``` +(a == null ? null : a.b).c +``` + +or + +``` +(a == null ? null : a.b.c) +``` + +I think it’s easy to see that the first one is much less useful. It would mean that + +``` +s?.Trim().Substring(x, y) +``` + +would always throw if `s` is `null`, thus entirely negating the benefit of `?.`. Clearly, it should mean the latter. + +This does not make it a right-associative operator. Instead, it means that the grammar needs to define “chains” of applications of this operator. In particular, + +``` +a?.b.c +``` + +should NOT be parsed as + +``` +MemberAccessExpression: + Safe = false + MemberName = c + Instance = MemberAccessExpression: + Safe = true + MemberName = b + Instance = a +``` + +but rather as something like: + +``` +MemberAccessExpression: + Instance = a + Chain = [ + [ Safe = true, MemberName = b ], + [ Safe = false, MemberName = c ] + ] +``` + +This way, the effect of the null check propagates through the chain of member accesses as the programmer intended. + +This approach is extensible, too. The same syntax can be used for safe indexing and safe invocation. For example: + +``` +myList?.Items?[0].SomeDelegate?(x) +``` + +would mean the effective equivalent of: + +``` +myList == null ? null : + myList.Items == null ? null : + myList.Items[0].SomeDelegate == null ? null : + myList.Items[0].SomeDelegate(x) +``` + +but without the repeated reevaluation. Note that the programmer can easily short-circuit the entire chain (as is clearly intended), while simultaneously communicating that some accesses (in this case, the result of myList.Items[0]) should not be null-checked. + + +--- + +On Apr 3, 2014 at 8:57 PM @pminaev wrote: + +> When passing a value as a parameter to a method, wouldn't you want to let the method decide how to best handle the null argument? + +Generally speaking, no, because by convention most would throw ArgumentNullException rather than return null. + +> If the method doesn't handle the null argument the same way as the caller would prefer, the current syntax is explicit and isn't really much of a nuisance. + +Currently, you rather have to write: +``` +return s != null ? Trim(s) : null; +``` +or maybe rather: +``` +return s == null ? null : Trim(s); +``` +(so that the most important expression is visually at the end, and better separated) + +It's not too bad on its own, yes. The problem is that it doesn't chain well with multiple invocations. Granted, the `?:` syntax doesn't do so, either - to chain, it would need some way to capture the expression on the left side, and then refer to it on the right side. E.g. with `it`: +``` +return s ?: TrimLeft(it) ?: TrimRight(it); +``` +This also has the bonus effect in that `s` can then be an arbitrary complex expression with side-effect, like `Foo(s1+s2)`. + +I suppose at this point it all just becomes a thinly veiled request for `let` (or other way to bind variables in expressions). E.g. assuming we had `let ... in ...`, this could be written as: +``` +return let it = Foo(s1+s2) in it == null ? null : let it = TrimLeft(it) in it == null ? null : TrimRight(it) +``` +and then perhaps with a null-propagating `let?` version: +``` +return let? it = Foo(s1+s2) let? it = TrimLeft(It) let? it = TrimRight(it) in it; +``` + +Though I'd still like some syntactic sugar for the most common "map left side to something convenient" operation. Say, `|>` could be the non-null version: +``` +return (s1 + s2) |> (it.Length > 10 : it.Substring(0, 10) : it); +``` +and then `?|>` is the null propagating one: +``` +return Foo(s) ?|> TrimLeft(it) ?|> TrimRight(it); +``` + +Yes, this is definitely getting much more complicated than what I originally had in mind... + +--- + +On Apr 4, 2014 at 4:57 PM @emn13 wrote: + +If something like this gets implemented, it would be considerably more powerful if it were implemented as a language pattern (ala LINQ) rather than as a null-specific fix. + +I'd absolutely love to use this kind of technique for other types, e.g. option types, possibly failing tasks, etc. + +For example, I've used an option-like type to communicate a value or an annotated error condition. Doing so in C# now is doable but fairly messy (also because there's no really clean way to implement a discriminated union), but potentially quite valuable. + + + +--- + +On Apr 4, 2014 at 8:57 PM @ljw1004 wrote: + +**pminaev wrote:** +> I suppose at this point it all just becomes a thinly veiled request for `let` + +Just to note, LET can be achieved by a combination of 'declaration expressions' (already in the Roslyn preview) and 'expression sequences' (not yet implemented). It'd look like this: + +``` cs +var x = (var s = expr; s == null ? "" : TrimLeft(s)); +``` + + + +--- + +On Apr 4, 2014 at 8:59 PM @ljw1004 wrote: + +**emn13 wrote:** +> If something like this gets implemented, it would be considerably more powerful if it were implemented as a language pattern (ala LINQ) rather than as a null-specific fix. + + +emn13, that's an intriguing idea, but I don't have any clear idea of how to implement it. Could you spell out a bit more what you're thinking? For instance, in this example, what kind of pattern would `s?.Length` expand to? +``` cs +string s = strings.GetFirstOrDefault(); +int? len = s?.Length; +``` + + + +--- + +On Apr 5, 2014 at 4:46 PM @Olmo wrote: + +I've been a ?. operator promoter since a long time ago but I didn't though about the precedence problem. Now I'm concerned. + +So, imagine you have a chain like this and you're brave enough to ignore Law of Demeter + +``` C# + Customer.Address.Country.IsoCode +``` + +You trust that you have a customer, addresses have a country, and countries have an IsoCode, but Address is optional in customer. + +There are two options: + +__left-associative:__ writing ?. contaminates all the next code + +``` C# +Customer.Address?.Country?.IsoCode2 + +``` +Cons: It clutters all the chain and promotes a 'just write ?. always' code style. +Pros: It's easy to reason about, every sub expression makes sense and has a type + +__right-associative:__ writing ?. just after each optional field + +```C# +Customer.Address?.Country.IsoCode +``` + +Pros: It makes clear with are the optional fields +Cons: You can not reason about the expressions easily anymore, and you have to take arbitrary decisions to when this chain ends: + +Lets imagine we have a GetCountry() extensions method on IsoCode type. + +```C# + Customer.Address?.Country.IsoCode.GetCountry() +``` + +I assume this should be interpreted like + +```C# +Customer.Address?(.Country.IsoCode.GetCountry()) +``` + +But what if we want to use the extension method explicitly: + +``` +CountryExtensions.GetCountry(Customer.Address?(.Country.IsoCode)) +``` + +Then the chain will be broken. And what about operators, could this work? + +```C# +Customer.LastInvoice?.Amount # "€"; +``` +Note: # should be a plus symbol, codeplex problem + +And if the answer is yes, what about this? + +```C# +"$" # Customer.LastInvoice?.Amount; +``` + +--- + +On Apr 5, 2014 at 5:34 PM @Olmo wrote: + +We actually have a TryCC extension method defined like this: + +```C# + public static R TryCC(this T t, Func func) + where T : class + where R : class + { + if (t == null) return null; + return func(t); + } +``` + +The method takes advantage that you can call extension methods on null values. + +The thing is that with this approach the precedence is clear: + +```C# +Customer.Address.TryCC(a=>a.Country.IsoCode) +``` +And also has no problem with any other kind of expression that are not just a chain of dots. + +```C# +Customer.LastInvoice.TryCS(a=>a.Amount + "€") +``` + +The problems of course is the syntax clutter: instead of just a ? you have to write 12 characters!. + +```C# +Customer.LastInvoice?.Amount + "€" +``` + +__But maybe we could reduce the clutter...__ + +The reason I need to call it TryCC, instead of just Try, is because the compiler can not choose the right overload when there are class / structs constraint in generic methods TryCC stands for Class-Class. So I've a TryCS, TrySC, TrySS. The overloads that take nullables can be properly resolved. + +Even if overload resolution looks like a complicated part of the compiler, maybe adding that won't be that hard + +```C# +Customer.LastInvoice.Try(a=>a.Amount + "€") +``` +--> 2 characters removed + +Also, I think C# could have a better syntax for lambdas with just one parameter, what about using @ for it? + +The @ will be bound to the closes lambda if it has just one parameter, and possibly not valid inside nested lambdas, but could make many queries simpler: + +```C# +Invoices.Where(@.Amount > 10).Select(new { @.Id, @.Customer }) +``` + +And also improve strongly typed reflection, filling the missing memberof operator: + +```C# +PropertyInfo = Customer.Propery(@.Id) +``` +So it will look like this being used with Try method + +```C# +Customer.LastInvoice.Try(@.Amount + "€") +``` + +--> 3 characters removed + +So this solutions stills adds more clutter but is build on more general language features and solves the associativity proplem. What you think? + +--- + +On Apr 5, 2014 at 11:17 PM @Timwi wrote: + +I think you should avoid writing code like this: + +> ``` +> Customer.Address?(.Country.IsoCode.GetCountry()) +> ``` + +This is not valid code and it is not clear what is meant. However, I think I understand what you mean in this case. You’re asking how to call the extension method even if `Customer.Address` was `null`. The answer is of course parentheses: + +``` +Customer.Address?.Country.IsoCode.GetCountry() +``` + +would mean the effective equivalent of + +``` +var tmp = Customer.Address; +tmp == null ? null : tmp.Country.IsoCode.GetCountry() +``` + +(thus, GetCountry() is only called when tmp is not null), while + +``` +(Customer.Address?.Country.IsoCode).GetCountry() +``` + +would mean the effective equivalent of + +``` +var tmp = Customer.Address; +(tmp == null ? null : tmp.Country.IsoCode).GetCountry() +``` +(thus, GetCountry() is always called). + +- - - + +> The reason I need to call it TryCC, instead of just Try, is because the compiler can not choose the right overload + +Actually it can, with a trick. I have such an extension method too, and in my case it’s called `NullOr`. I think `Try` is a very bad name because `try` already means something unrelated in C#; `try` has to do with exceptions, but your extension method does not. + +The trick is that putting the constraints on the extension methods is not quite enough; you have to put the same constraints on the delegate types that you use as parameters. Here is my code: http://pastebin.com/vdS1uNu1 + +- - - + +> ``` +> Invoices.Where(@.Amount > 10).Select(new { @.Id, @.Customer }) +> ``` + +This code is highly ambiguous. How should the compiler decide which of the following you meant? + +``` +Invoices.Where(x => x.Amount > 10).Select(new { y => y.Id, z => z.Customer }) +Invoices.Where(x => x.Amount > 10).Select(y => new { y.Id, y.Customer }) +x => Invoices.Where(x.Amount > 10).Select(new { x.Id, x.Customer }) +``` + +However, we are getting side-tracked from the topic of this thread. I think we should discuss only the `?.` feature here, not lambda expressions or type inference. Consider opening a new thread for those separate topics. + +--- + +On Apr 6, 2014 at 1:19 AM @Olmo wrote: + +Hi Timwi, + +> The trick is that putting the constraints on the extension methods is not quite enough; you have to put the same constraints on the delegate types that you use as parameters. Here is my code: http://pastebin.com/vdS1uNu1 + +You are the man! This trick for solving the overload it's really clever. How you come up with it? It will have saved me lot of time explaining the difference of the TryCC overloads if I knew it years ago! + +The fact that you can parenthesize simplifies the problem a little bit and relieves my concerns about the operator, so I like it more now. + +Still, the expression + +``` +MemberAccessExpression: + Instance = a + Chain = [ + [ Safe = true, MemberName = b ], + [ Safe = false, MemberName = c ] + ] +``` + +It's a bad solution. It's really not about chains of MemberAccessExpression. MethodCallExpression at least should also be included in the same chain, making the AST quite strange. + +This AST will also propagate to the System.Expression namespace, breaking LINQ providers (except if the chain happens only if there's a ?. operator, in which case we will 'only' need to add some code duplication for MemberAccessExpression/MethodCallExpression). + +Also, the solution is limited in the sense that it only contemplates the . operator, ignoring binary operators, method invocation as an argument (i.e.: Math.Abs()), etc... that will be also useful. + +So, I'm still not convinced. I prefer my solution with one argument lambdas, even if, just for this case, the lambda could be compiled away and inline in this same method. + +I've copied your concerns and answered to them here: +__Sorter syntax for lambda expression with just one parameter__ https://roslyn.codeplex.com/discussions/541330 + + +--- + +On Apr 6, 2014 at 5:03 PM @Timwi wrote: + +I admit I hadn’t thought of the `System.Expression` namespace. That would be an interesting thing to think about. However, it does not negate the parse tree I’ve suggested; it’s the only way this can be parsed such that it is in line with the programmer’s intention. + +> It's really not about chains of MemberAccessExpression. + +I tried to keep it simple by sticking to member access. However, in my proposal, the same AST node type would be used for method and indexer invocations. Thus my own example of + +``` +myList?.Items?[0].SomeDelegate?(x) +``` + +would turn into a parse tree resembling the following: + +``` +MemberAccessExpression: + Instance = myList + Chain = [ + [ Safe = true, Member = get_Items() ], // Property getter + [ Safe = true, Member = get_Item(int), Args = [ 0 ] ], // Indexer getter + [ Safe = false, Member = get_SomeDelegate() ], // Property getter + [ Safe = true, Member = Invoke(Foo), Args = [ x ] ], // Delegate .Invoke + ] +``` + +Can you explain further why you think it’s “a bad solution” and why “it’s really not about chains”? In my mind, it is all about chains: you need the null-checking semantics to “seep into” all subsequent member/indexer access/invocations until the point up to where the programmer intends the null-check to go (= the end of the chain). + +It is true that I have not addressed binary operators; however, I think this is completely in line with the programmer’s intention. If you write an expression like this: + +``` +person.?Address.City ?? "" +``` + +which of the following interpretations do you think is the most likely intention? + +``` +// ① We’ve already discarded this one: it would still throw if person is null +// and thus negate the benefit of the feature +var tmp = person == null ? null : person.Address; +tmp.City ?? "" + +person.?Address.City ?? "" +└─────────────┘ extent of the perceived chain + +// ② I think this is the most likely intention +var tmp = person == null ? null : person.Address.City; +tmp ?? "" + +person.?Address.City ?? "" +└──────────────────┘ extent of the perceived chain + +// ③ This is the only other possible interpretation. +// In this example, it is clearly not what was intended. +person == null ? null : (person.Address.City ?? ""); + +person.?Address.City ?? "" +└─────────────────────────────────┘ extent of the perceived chain +``` + +I’ve looked at various expressions with binary operators and I found interpretation ② above to be the most likely intention in all of them. Can you think of a case in which interpretation ③ would be more likely intended than ②? + +--- + +On Apr 6, 2014 at 5:45 PM @mirhagk wrote: + +I think 2 in the above example should be the intended interpretation. The place where this operator is going to be used a lot by my team is with EF and partial classes, something like: + + partial class Customer + { + CountryName + { + get + { + return this.Address.?Country.?Name??"None"; + } + } + } + +Where the Null propagating operator is combined with the default value one to walk a chain, and return a default if any part of the chain doesn't exist. + +--- + +On Apr 6, 2014 at 6:02 PM @pazucha wrote: + +This: +``` +x?.y?.z +``` + +Should mean this: +``` +(x == null ? null : (x.y == null ? null : x.y.z)) +``` + +Or more specifically: +``` +if(x==null) return null; +if(x.y==null) return null; +return x.y.z; +``` + +Left asociative if correct. +?. should have the same priority as . + +--- + +On Apr 6, 2014 at 8:07 PM @Olmo wrote: + +Hi Timwi, + +I completely agree with you that the right-associative is a better option to than left-associative. Code should have ?. only on the optional fields, not contaminating the rest of the chain. + +But there are still two problems with the implementations. + +While I'm not an expert in the Roslyn nodes, I'm quite used to System.Expresion and I think we can reason using System.Expression terminology with no mismatch. + +So, before expressing my concerns: + +__A brief introduction to System.Expression using some pseudo-code for sort__ + +```C# +2 + 3 +BinaryExpression( + ConstantExpresson(2), + ConstantExpression(3), + +) + +person.Name +MemberExpression( + ConstantExpression(person), + PropertyInfo("Name")) + +person["Name"] // indexer +MemberExpression( + ConstantExpression(person), + PropertyInfo("Item"), + ConstantExpression("Name")) + +person.ToString() +MethodCallExpresion( + ConstantExpression(person), //object + MethodInfo("ToString"), //method + null) // no aditional arguments + +Database.Save(person) //Extension method used as static method +MethodCallExpresion( + null, // no instance + MethodInfo("Save"), //method + ConstantExpression(person)) // person is the first parameter! + +person.Save() //Extension method!! +MethodCallExpresion( + null, // no instance + MethodInfo("Save"), //method + ConstantExpression(person)) // person is the still first parameter! +``` + +Note: I've choosed `ConstantExpression(person)` instead of `ParameterExpession("person", typeof(Person))` for pedagogic purposes + +I didn't include delegate invocation because is looks that is not going to be included, fall-backing to calling the Invoke method, but will be just one case more. + +Let's see how a complex expression is represented then + +```C# +Save(person.Name["0"].ToString().Save()) + +MethodCallExpresion( + null, + MethodInfo("Save"), + MethodCallExpresion( + null, + MethodInfo("Save"), + MethodCallExpression( + MemberExpression( + MemberExpression( + ConstantExpression(person), + ProperyInfo("Name")), + ProperyInfo("Item"), + ConstantExpression("0")) + MethodInfo("ToString")))) +``` + +Every expression node has a type, some child nodes, and defined rules that are checked for composition in a type-safe manner. + +Also, the same node (i.e MethodCallExpression) is used right now in every single method call, independently of whether it is inside of a BinaryExpression, LambdaExpression, etc.. + +__Concern n°1: Expression node duplication__ + +I've two problems with your MemberAccessExpression. + +First, a superficial one: it should be called DotChainExpression, because it's not just about MemberExpression. MethodCallExpression are just as important in the Chain. + +Secondly, a deeper one: the nodes you have on your chain, do not form a tree but a list. Due to that, new alternative nodes have to be created: + +```C# +Save(person?.Name["0"].ToString().Save()) + +MethodCallExpresion( + null, + MethodInfo("Save"), + DotChainExpression( + MethodCallChainElement(null, MethodInfo("Save"), ??), + MemberChainElement(??, ProperyInfo("Item"), ConstantExpression("0")), + MethodCallChainElement(??, MethodInfo("ToString")))) +``` + +This duplication means that now every refactoring, linq provider, etc.. that has to do with MethodCallExpression, has to think also about MethodCallChainElement because they are structurally different classes. Same for MemberExpression. + +This is a bad AST design, but we could solve it retaining the tree structure, defining DotChainExpression like this: + +```C# +DotChainExpresion( + leftExpresion, + parameterExpression, + rightExpression) +``` + +```C# +Save(person?.Name["0"].ToString().Save()) + +var nnp = ParameterExpression("notNullPerson", typeof(Person)); + +MethodCallExpresion( + null, + MethodInfo("Save"), + DotChainExpresion( + ParameterExpression("person", typeof(Person)), + nnp, + MethodCallExpression( + null, + MethodInfo("Save"), + MethodCallExpression( + MemberExpression( + MemberExpression( + nnp + ProperyInfo("Name")), + ProperyInfo("Item"), + ConstantExpression("0")) + MethodInfo("ToString"))))) +``` + +The additional `ParameterExpression` represents the cached value of the `leftExpression`, that is calculated just once. + +This solution will work at the expression level, but requires that the compiler takes some (in my opinion) arbitrary decisions. + +A DotChainExpresion expression will be created on every ?. operator, taking in `rightExpression` any node that is: + + * A MemberExpression for witch the ?. is already in the Expression propery (left), whether it is a normal access propery or a parametrized one (Indexer) + * A MethodCallExpression for witch the ?. is already in the chain on the Object propery (left) or in the first argument of an extension method call _that is used as an Extension method_. + +So the compiler will need to convert: + +```C# +person.Contry.IsoCode +MemberExpression( + MemberExpression( + ConstantExpession(person), + ProperyInfo("Country")), + ProperyInfo("IsoCode")); +``` + +Into something like this + +```C# +person.Contry.IsoCode + +var nnp = ParameterExpression("notNullPerson", typeof(Person)); +DotChainExpresion( + ConstantExpession(person), + nnp, + MemberExpression( + MemberExpression( + nnp, + ProperyInfo("Country")), + ProperyInfo("IsoCode"))); +``` + +Look how the ConstantExpession(person), that before was a inner children, now is almost parent!. + +But even at the complexity of the AST parser, that should work, so my concern n° 1 could be fixed. + +__Concern n° 2: User case limitations__ + +Due to the (in my opinion) arbitrary decisions to determine what should be added in the ParameterExpression rightExpression argument, the developer can not use the operator in many useful situations. + +Maybe your example, coallesce operator, is not one of those but there are others, here there are three: + +```C# +Invoice?.Amount.Abs() //doesn't work because is not an extension method + +var amount = Invoice?.Amount; +amount == null ? null : Math.Abs(amount); //horrible fallback + +Invoice.Try(a=>Math.Abs(a. Amount)) //Using Try method is not a problem +Invoice.Try(Math.Abs(@Amount)) //And with short one param lambda expression even better +``` + +```C# +Invoice?.Amount + " €" //will write " €" instead of "" when no invoice + +var amount = Invoice?.Amount; +amount == null ? null : (amount + " €"); //horrible fallback + +Invoice.Try(a => a.Amount + " €") //Using Try method is not a problem +Invoice.Try(@Amount + " €") //And with short one param lambda expression even better +``` + +```C# +person?.Sex == Male? "M" : "F" //will return "F" when person is null + +var sex = person?.Sex; +sex == null ? null : (sex == Male ? "M" : "F"); //horrible fallback + +person.Try(p => p.Sex == Male ? "M" : "F") //Using Try method is not a problem +person.Try(@p.Sex == Male ? "M" : "F") //And with short one param lambda expression even better +``` + +So my concern is that, under the covers, the 'chain after the ?. operator' is just a hidden lambda with some arbitrary limitations. + +Actually, the AST can be easily transformed: + +```C# +DotChainExpresion( + leftExpresion, + parameterExpression, + rightExpression) +``` + +Is equivalent to my solution + +```C# +MethodCallExpression( + null, + MethodInfo("Try"), + leftExpresion, + LambdaExpression( + parameterExpression, + rightExpression) +``` + +Buy my solution let's any kind of expression on rightExpression, while DotChains only allows some MethodCallExpresions, and all the MemberExpresson, missing all the others (there are like 20). + +The DotChainExpression is sorter, it's true, but implementing sorter lambda expression with just one parameter will reduce the difference, and improve other unrelated scenarios (LINQ queries, strongly typed reflection, etc..). This is a good symptom for a language feature. + +PD: I hope C# team doesn't ban me after this long post + +--- + +On Apr 7, 2014 at 1:34 AM @Timwi wrote: + +I fully understand you. Of course you are right that your `.Try`/my `.NullOr` can do everything that `?.` can do, but `?.` can’t do everything those methods can do, making `?.` feel limited. + +If you want to compile `?.` into Expression types that already exist, you can actually do that even without `Try`/`NullOr` by having the compiler generate the entire null check: + +``` +var p = Expression.Parameter(typeof(Person)); +var rightExpression = ...; // compile the rest of the chain using “p” for the instance object + +var ncp = Expression.Parameter(typeof(Person)); +Expression.Invoke( + Expression.Lambda( + Expression.Condition( + Expression.MakeBinary(ExpressionType.Equal, ncp, Expression.Constant(null)), + Expression.Constant(null), + Expression.Invoke(Expression.Lambda(rightExpression, p), ncp)), + ncp), + leftExpression) +``` + +This would be a possible way to represent the `?.` expression without introducing new expression node types. + +However, it would complicate the situation for LINQ providers, wouldn’t you agree? They would now have to support arbitrary lambda calculus. Do they already do that? I haven’t tried feeding them expression trees with nested lambda invokes. Given that we’re discussing the `?.` feature, which *is* limited to specific kinds of null checks, I like to think about how to design an Expression API for it that doesn’t require this complexity. + +I envisage the `DotChainExpresion` constructor to look like this: + +``` +DotChainExpresion( + instanceExpresion, + params DotChainElement[] elements) +``` + +and your example expression would be represented as follows: + +``` +Save(person?.Name["0"].ToString().Save()) + +MethodCallExpresion( + null, // assuming the outer Save is a static method + MethodInfo("Save"), + DotChainExpression( + personParameter, // = ParameterExpression("person", typeof(Person)) + // (params array starts here) + MemberChainElement(ProperyInfo("Item"), ConstantExpression("0")), + MethodCallChainElement(MethodInfo("ToString")), + MethodCallChainElement(MethodInfo("Save")))) // inner Save, which is can be an instance method or a static (extension) method +``` + +In this example, I’m assuming that `DotChainElement` would be an abstract class from which `MemberChainElement` and `MethodCallChainElement` would be derived. It could also simply be a single class, `DotChainElement`, that takes any `MemberInfo`. This detail could be debated separately. Overall, this is a perfectly fine way to represent this specialized syntax. + +> This duplication means that now every refactoring, linq provider, etc.. that has to do with MethodCallExpression, has to think also about MethodCallChainElement because they are structurally different classes. Same for MemberExpression. + +Refactoring tools will have to be able to work with the new syntax no matter which way you represent it. + +It is true that introducing a new node type will require that LINQ providers add new code to handle it. However, in the alternative they would have to add (significantly more complex) code to handle lambda calculus. (Note that `MemberChainElement` and `MethodCallChainElement` are *not* expression nodes because they are not expressions. Instead they are simple data classes that describe the elements that make up a `DotChainExpression`. LINQ providers would need to implement one new node type: `DotChainExpression`.) + +--- + +On Apr 7, 2014 at 8:11 AM @Olmo wrote: + + +I haven't add support for our Try method because it is useless in SQL. All the . operators are already implemented as left joins, so equivalent to ?. operators. There's no easy way to throw NullReferenceException on the row of a null FK on SQL. + +But this is only true for SQL LINQ providers, for other LINQ providers it will make perfect sense, and Roslyn refactorings and other language services should face similar problems. + +So, with my experience using LINQ providers: + +1.- I will much happier implement support for the Try method. Implementing lambda application and replacement is something you already have to do. All the LINQ queries use lambdas and queries can be nested! I could add suppor in line 1 hour. + +2.- Implementing + +```C# +DotChainExpresion( + leftExpresion, + parameterExpression, + rightExpression) +``` + +won't be that hard either, I will have to support one node type more, DotChainExpresion, but the rest of the code won't be affected. 2 Hours. + +3.- Implementing + +```C# +DotChainExpresion( + instanceExpresion, + params DotChainElement[] elements) +``` + +Beause of the duplication of MemberChainElement and MethodCallChainElement. This is MUCH harder. Lots of code will be affected because MethodCallExpression and MemberExpression are quite used already, and will have to be duplicated. Lots of maintainability problems in the future. And also I still have some questions: + +* How you know witch is the previous sub-expression in the chain for instance methods / extension methods. +* Who makes the Type checking on DotChainExpression? DotChainExpression itself or each XXXChainElement? + +I think, if DotChainExpresion should be implemented, alternative 2 is much simpler and preserves the tree structure. + +So +> Refactoring tools will have to be able to work with the new syntax no matter which way you represent it. + +Number 3 is much harder to work with than number 2 + + + +--- + +On Apr 7, 2014 at 5:18 PM @eldritchconundrum wrote: + +Whoa... I never imagined that right-associative could even be an option. +It is indeed more powerful, but also much more complex, since we have to understand and remember the rule for deciding when the "dotchain" end. + +People would never expect that such a simple-looking operator would have such a subtle behaviour. +That's why I think left-associative is better. + +Yes, that means we would have to write a?.b?.c "instead" of a?.b.c, but I don't think this will be a problem to anyone in practice. + + +--- + +On Apr 7, 2014 at 7:54 PM @KathleenDollard wrote: + +It’s fantastic to have language design at the community level. But this is a gnarly problem and at the end, I think we’re going to have to trust somebody to just make the call. That’s why ultimately we have language keepers – and we are lucky to have good ones. + +The subtlety of this problem is such that I’m not seeing today (although I thought I understood this problem) why 2b in the original example is a compile time error with left associative. Lucian or someone, could you explain that? + +These two things make me feel left associative is the best way. Not because it is good, but because right associative is flawed and the feature is too valuable to skip. + +``` +__var x = GetCustomer1()?.extraRef?.name?.Length +==> +var explainingVariable = GetCustomer1()?.extraRef; +var x = explainingVariable?.name?.Length; +_``` +_ +Programmers would expect this to be a valid refactoring -> Left associative + +Well stated by Olmo + +``` +___left-associative: writing ?. contaminates all the next code +Customer.Address?.Country?.IsoCode2 + +Cons: It clutters all the chain and promotes a 'just write ?. always' code style. +Pros: It's easy to reason about, every sub expression makes sense and has a type + +_right-associative: writing ?. just after each optional field +Customer.Address?.Country.IsoCode +Pros: It makes clear with are the optional fields +Cons: You can not reason about the expressions easily anymore, and you have to take arbitrary decisions to when this chain ends: + +__``` +“You cannot reason about the expressions easily anymore” Therefore, it does not work in the language. Ultimately the most important thing for a computer language is that it does the think the humans think it will do. + +The extension method question is interesting (also raised by Olmo). With a GetCountry extension, these are no longer equivalent (the first skips the call, so is more efficient). + +``` +Customer.Address?.Country.IsoCode.GetCountry() +CountryExtensions.GetCountry(Customer.Address?(.Country.IsoCode)) + +``` +There might be languages where the correctness of right associative or the lack of pollution with many ?. would supersede clarity. But I don’t think C# or Visual Basic are those languages. I think the most important factor in language decisions are whether it results in code that is easy for a slightly above average programmer to understand and reason about. + + + +--- + +On Apr 7, 2014 at 9:26 PM @PauloMorgado wrote: + +I've been back and forth with this because I really want to keep it simple. + +Most C# operators are left-associative, so my first instinct is to be left-associative. Even with the possible clutter of __?.__s. + +However, since the null coalescing operator (__??__) is right-associative and this seems to be the closest in semantics with the null propagating (__?.__) operator, I think the null propagating operator should be right-associative. + +--- + +On Apr 7, 2014 at 11:35 PM @Olmo wrote: + +I'm not happy with the left/right associative terminology. + +I think it should be used only for binary (or ternary) expressions: + +```C# +a + b + c + d +left-associate: ((a + b) + c) + d +right-associative: (a + (b + (c + d))) +``` + +But MemberExpression does not consist on two expression. Instead it has an Expression and a Member (Field or Property). The member has a type but not a type __on its own!__. + +```C# +a.b.c.d +left-associate: ((a.b).c).d +right-associative: a.(b.(c.d)) -> makes no sense +``` + +So I think a better nomenclature is: + +__1.- Trivial-polluter:__ +```C# +//You should write +GetCustomer1()?.extraRef?.name?.Length; + +//And will get compiled to +var c = GetCustomer1(); +var er = c == null ? null : c.extraRef; +var n = er == null ? er.name; +n == null ? null : (int?)n.length +``` + +__2.- Complex-clean:__ +```C# +//You should write +GetCustomer1()?.extraRef.name.Length; + +//And will get compiled to: +var c = GetCustomer1()? +c == null ? null : (int?)c.extraRef.name.Length +``` + +As I've stand. If I'll have to chose I'll go for 2 but I'll rather choose: + +__3.- Optimized lambda:__ +```C# +//You should write +GetCustomer1().Try(c => c.extraRef.name.Length); +//Or +GetCustomer1().Try(@.extraRef.name.Length); + +//And will get compiled to: +var c = GetCustomer1()? +c == null ? null : (int?)c.extraRef.name.Length +``` + +It's a little longer but: +* It's more general +* The developer just has to learn a new method, not a language feature +* The compiler could understand the pattern and remove the lambda altogether for performance reasons. With meta-programming for method invocations even a library could do it! + +--- + +On Apr 7, 2014 at 11:51 PM @ljw1004 wrote: + +Olmo, how would you write the "Try" method version when name might be null as well and so needs its own null-check? + +--- + +On Apr 7, 2014 at 11:55 PM @supercat wrote: + +I'm probably a bit late to the party, but it seems that code which would want the value of `foo.bar` when `foo` is not null would, in many cases, want a value other than `null` when `foo` is `null`. Is there any planned way to handle cases where the default value should be something other than `null` (as would be necessary if the expression result was supposed to be a value type, or if `null` was a legitimate value for the member to be accessed, but should not be the default value [precluding use of `??` after `?.`? + +I would suggest that `foo?.bar : boz` should be equivalent to `(var temp=foo; temp != null ? temp.bar : boz)`, and that such equivalence should hold *even when foo.bar is a non-nullable value type*. It may be helpful to have the `: boz` expression be optional in cases where there would only be one sensible way of parsing the resulting expression, but forbid compilation of code which could be parsed in two or more *semantically-different* ways. Would that seem practical? + +--- + +On Apr 8, 2014 at 12:11 AM @Olmo wrote: + +ljw1004, it's written up + +```C# + public static R TryCC(this T t, Func func) + where T : class + where R : class + { + if (t == null) return null; + return func(t); + } +``` + +Works because you can call extension method on null values. + +--- + +On Apr 8, 2014 at 2:21 AM @BillMcC wrote: + +Consider the refactoring part with the value type example: + +int? age2b = GetCustomer2()?.extraStruct.age; + +This would refactor t, assuming left associativity: + +var explaingVar = GetCustomer2()?.extraStruct; +int? age2b = explainingVar?.__Value__.age; + +Note the injection of .Value + +So maybe, regardless of left or right, the ?. should be extended to nullable types so as the refactoring becomes: + +int? age2b = explainingVar?.age; + +And hence ?. could be used on nullable type to mean if the nullable type is null, return null, otherwise call/get the member on the Value of the nullable type + +--- + +On Apr 8, 2014 at 2:44 AM @lwischik wrote: + +**Olmo wrote:** +> ljw1004, it's written up ... + +Okay, so writing it out in full... +1 = left-associative / trivial-polluter +2 = right-associative / complex-clean +3 = optimized lambda + +``` cs +// I want to do null-checks on GetCustomer1(), and on extraRef, and on name +1: var x = GetCustomer1()?.extraRef?.name?.Length; +2: var x = GetCustomer1()?.extraRef?.name?.Length; +3: var x = GetCustomer1().Try(c => c.extraRef).Try(r => r.name).Try(n => n.Length); + +// I want to do null-checks on GetCustomer1(), but skip them on extraRef and on name +1: var x = GetCustomer1()???? *impossible* +2: var x = GetCustomer1()?.extraRef.name.Length; +3: var x = GetCustomer1()?.Try(c => c.extraRef.name.Length); +``` + +Is that right? + + +--- + +On Apr 8, 2014 at 8:14 AM @Olmo wrote: + +lwischik, it's not really about what you want to do as a developer, but about how they will design the feature. + +Let's imagine imagine that we were using only __structs__ and __nullable struct__ here, so we're able to reason about types: + + __1.- left-associative / trivial-polluter__ + +Every time we have a T?, and T has a member (or method) returning P, we can use .? operator to get a P? + +```C# +GetCustomer1() //returns Customer? + +GetCustomer1()?.extraRef // is a ExtraRef?, even if the propery is of type ExtraRef +GetCustomer1()?.extraRef?.name //is a string?, even if the property is of type string +GetCustomer1()?.extraRef?.name?.Length //is a int?, even if the property is of type int +GetCustomer1()?.extraRef?.name?.Length == 1//compares int? and int, returning a boolean +``` + +The implementation is __trivial__, but once you start using ?. in a chain, you have to use it till the end, __polluting__ the rest of the line. Is kind of similar to Haskell IO + +If we compare this behavior with my Try method, it will be like this: + +```C# +GetCustomer1() //returns Customer? + +GetCustomer1().Try(c=>c.extraRef) +GetCustomer1().Try(c=>c.extraRef).Try(er=>er.name) +GetCustomer1().Try(c=>c.extraRef).Try(er=>er.name).Try(n=>n.Length) +GetCustomer1().Try(c=>c.extraRef).Try(er=>er.name).Try(n=>n.Length) == 1 //The expansions stops here because there are no more ?. operators +``` + + __2.- right-associative / complex-clean__ + +```C# +GetCustomer1() //returns Customer? + +GetCustomer1()?.extraRef +// Is a ExtraRef?, except if the following expression also contains a . or .? operator, that will be considered of type ExtraRef. +// Since the next expression is going to be a . operator (name), it will be considered of type ExtraRef + +GetCustomer1()?.extraRef.name +// Is a string?, except if the following expression also contains a . or .? operator, that will be considered of type string +// Since the next expression is going to be a . operator (Length), it will be considered of type ExtraRef + +GetCustomer1()?.extraRef?.name?.Length +// Is a int?, except if the following expression also contains a . or .? operator, that will be considered of type string +// Since this time is not the case, the final type of the expression is int? + +GetCustomer1()?.extraRef?.name?.Length == 1 +//compares int? and int, returning a bool +``` + +The trick here is that there's a hidden lambda, that gets larger every time you use a dot but not for any other operator. The result is __clean__ but is __complex__ to understand. + +This is how it will compare with Try method. + +```C# +GetCustomer1() //returns Customer? + +GetCustomer1().Try(c=>c.extraRef) +GetCustomer1().Try(c=>c.extraRef.name) +GetCustomer1().Try(c=>c.extraRef.name.Length) +GetCustomer1().Try(c=>c.extraRef.name.Length) == 1 //The expansions stops here because there are no more ./?. operators +``` + +Notice how the behavior is much clearer using Try method, because we know there the magic ends: when you close the parenthesis. + +Notice also, how the Try method is more expressive because you can write: + +```C# +GetCustomer1().Try(c=>c.extraRef.name.Length == 1) +//returns bool? that will be +//if GetCustomer1()is null, returns null +//else if name.length is 1, returns true +// else returns false +``` + +I think, after many explanation, we can agree that this feature is way to complex and case-specific for being implemented + +Let's just: +* Add Try method in the BCL, maybe with another name like NullOr of you prefer. +* Teach the C# compiler to, for any method named Try that takes a lambda, avoid compiling the lambda all together and translate it to inline conditionals instead. +* Simplify the lambda syntax so we can write +```C# +GetCustomer1().Try(@.extraRef.name.Length == 1) +``` +as proposed here https://roslyn.codeplex.com/discussions/541330 + +--- + +On Apr 8, 2014 at 8:42 AM @vas6ili wrote: + +This might be a little confusing for delevopers, but why not support both? +``` C# + a?.b // left-associative + a.?b // right-associative +``` + +--- + +On Apr 8, 2014 at 9:52 AM @Olmo wrote: + +An interesting idea. But two complex things don't make a simple one. + +--- + +On Apr 8, 2014 at 11:37 AM @Timwi wrote: + +As an aside, Olmo did not answer ljw1004’s question: + +> Olmo, how would you write the "Try" method version when name might be null as well and so needs its own null-check? + +The answer is: + +``` +GetCustomer1().Try(c => c.extraRef.name.Try(n => n.Length)) +``` + +--- + +On Apr 8, 2014 at 11:41 AM @PauloMorgado wrote: + +The design guidelines for extension methods are that they should not accept null values for the extended object (in fact extension methods are used to extend objects of some type and not the type itself) because a method with the same signature might be added to the type in the future causing a NullReferenceException to be thrown. + +Adding a Try (or NullOr or whatever) extension method to the BCL would require explicit namespace importing or explicit class reference if a Try method was added to the type of the object we are "trying". + +I like right associative because it might be a developer's decision that not all parts of the chain can be nullable. I might wan to write __persion?.Name.FirstName.Lengh__ because if __person__ is not null, if __Name__ or __Name.FirstName__ something very bad happened. But, in the end, the result will always be of type __int?__. + +And this needs to be refactorable. It's not hard, but the result is not what you would expect. + +``` +var l = persion?.Name.FirstName.Lengh + +``` +can be refactored into: + +``` +var n = person?.Name; +var fn = n?.FirstName; +int? l = fn?.Length + +``` + +which is do not mean the same because Name and FirstName can now be null. + + +--- + +On Apr 8, 2014 at 11:56 AM @Olmo wrote: + +> The design guidelines for extension methods are that they should not accept null values for the extended object (in fact extension methods are used to extend objects of some type and not the type itself) because a method with the same signature might be added to the type in the future causing a NullReferenceException to be thrown. + +Yup, it will break the guidelines. + +> Adding a Try (or NullOr or whatever) extension method to the BCL would require explicit namespace importing or explicit class reference if a Try method was added to the type of the object we are "trying". + +Good point but also applicable to all LINQ. Someone could have had a Select, Where, etc.. method before. + +In this case the instance method should be used, of course. + +--- + +On Apr 8, 2014 at 12:02 PM @PauloMorgado wrote: + +**Olmo wrote:** +> Good point but also applicable to all LINQ. Someone could have had a Select, Where, etc.. method before. + +Someone could and can add any of the LINQ operators to types. As long as the signature and semantics are the same, the compiler will favor the instance methods over any extension method. + +But LINQ operators don't extend null as Try would. + +--- + +On Apr 8, 2014 at 12:03 PM @dsaf wrote: + +**Olmo wrote:** +> The method takes advantage that you can call extension methods on null values. + +This is a design flaw being exploited rather than an advantage. + +**Olmo wrote:** +> > The design guidelines for extension methods are that they should not accept null values for the extended object (in fact extension methods are used to extend objects of some type and not the type itself) because a method with the same signature might be added to the type in the future causing a NullReferenceException to be thrown. +> +> Yup, it will break the guidelines. + + +Please, don't! Guidelines are there not to be broken. Your Try method can already be implementing using the Maybe monad approach (I changed the name to reflect the intent - checking for nulls): + +``` +If.NotNull(GetCustomer1()).NotNull(c=>c.extraRef).NotNull(er=>er.name).Get(n=>n.Length) +``` + +It follows the pattern of other .NET Framework libraries: + +``` +Parallel.For(...)... +Task.Start(...)... +Enumerable.Range(...)... +``` + +**Olmo wrote:** +> Teach the C# compiler to, for any method named Try that takes a lambda, avoid compiling the lambda all together and translate it to inline conditionals instead. + +Did anyone do this sort of thing in any language ever (treating methods differently based on names)? It sounds like mixing the language and libraries. What about code that already has Try extension methods? + +PS: most definitely would not use TryCC, TryCS... naming pattern. There are two general kinds of types in .NET Framework: reference and value types. My personal preference is to use NotNull(...).Get(...) for reference values and NotNull(...).GetVal(...) for value types. + +--- + +On Apr 8, 2014 at 12:18 PM @Olmo wrote: + +The alternative is that someone have to come here and spend 3 hours trying to understand what the hell ?. does, for finally realizing that it's a partial ad-hoc feature. + +It's just too complicated. + +Another problem: + +``` +string str = null; + +str?.Length //type int? +str?.Length.Value //type int? +str?.Length.Value.Value //type int? +... +str?.Length.Value.Value.Value.Value.Value.Value //aaaaaaaaargggh! +``` + +Of course is solvable but I can feel the frustration. + + + +--- + +On Apr 8, 2014 at 12:24 PM @PauloMorgado wrote: + +Nope! + +Because: + +``` +string str = null; +int? l = str?.Length; +bool hv = l.HasValue; +int v = l.Value; +``` + +Nullable value types have special treatment in other parts of the language and CLR. the same will happen here. + +But you could write: + +``` +str?.Length?.Value?.Value?.Value?.Value?.Value?.Value +``` + +--- + +On Apr 8, 2014 at 12:30 PM @Olmo wrote: + +Another special case, as if this feature didn't have enough already! + +And, the type of the expression `str?.Length?.Value` is definitely typed to int after your special case, how are you going to write ?. after that? Another special case? + + + +--- + +On Apr 8, 2014 at 12:47 PM @Olmo wrote: + +Also: + +``` +string str = null; + +str?.Length //type int? +``` + +If the user hovers with the mouse over Length property the type will stand for int? but when he press . what the auto-complete should show? + +* int members +* int? members +* a mixture of both + +--- + +On Apr 8, 2014 at 12:56 PM @PauloMorgado wrote: + +That's not a special case for this feature. It's a normal special case for nullable value types. + +And, come to think of that, you couldn't do what I said you could. + +--- + +On Apr 8, 2014 at 12:58 PM @PauloMorgado wrote: + +Intellisense is context sensitive. In this context, the type of the resulting expression is int?. + +--- + +On Apr 8, 2014 at 1:09 PM @BillMcC wrote: + +``` + +str?.Length.Value.Value + + +``` + +That's not possible with either left or right associativity. + +With left associativity, the str?.Length is a nullable int. You could then call .Value on it, but it ends there. + +With right associativity you wouldn't be allowed the .Value: you'd have to end the right associativity with parenthesis, eg: +(str?.Length).Value +Again, it ends there. + +--- + +On Apr 8, 2014 at 1:13 PM @pazucha wrote: + +I would allow ?. operator to be usable only on reference types on the left. Using it on nullable value types like int? is almost useless, leading to confusion of the type. + +--- + +On Apr 8, 2014 at 1:23 PM @BillMcC wrote: + +**pazucha wrote:** +> I would allow ?. operator to be usable only on reference types on the left. Using it on nullable value types like int? is almost useless, leading to confusion of the type. + +So how would you deal with Customer.Location.Longitude where Location is a Nullable type ? + +--- + +On Apr 8, 2014 at 1:38 PM @Olmo wrote: + +BillMcC, + +> Again, it ends there. + +You're right, I confused the behavior. + +But the fact that I confused it, after 1 day of discussion, makes it clear that the feature is quite hard to grasp. + +What a trick! Winning the argument by loosing it :) + +--- + +On Apr 8, 2014 at 1:55 PM @Olmo wrote: + +There's still the Intellisense issue: + + +```C# +Customer?.DateOfBirth +``` + +##What Intellisense should indicate when you hover DateOfBirth? + +__1.- That it's a DateTime__ + +Because you can continue doing + +```C# +Customer?.DateOfBirth.Year +``` + +__2.- That it's a DateTime?__ + +Because you can continue doing + +```C# +Customer?.DateOfBirth.Value +``` + +because a var will infer DateTime? + +```C# +var date = Customer?.DateOfBirth +``` + +And because it will be added to TimeSpan with null propagation semantics, returning, again, DateTime? + +```C# +Customer?.DateOfBirth + new TimeSpan(2, 0, 0) +``` + +##And what Intellisense should show when you write the next dot? + +* DateTime Members (Year, Month...) +* DateTime? Members (Value and HasValue) +* a mixture of both + +--- + +On Apr 8, 2014 at 1:56 PM @BillMcC wrote: + +**Olmo wrote:** +> +> But the fact that I confused it, after 1 day of discussion, makes it clear that the feature is quite hard to grasp. +> + +It is tricky to discuss alternatives: a prime example is even Lucian confused some of his first examples. I feel I have a good grasp of the concepts, but I too get confused when there are different alternatives suggested, especially if they have different "quirks" about them. But if there was only one implementation, then the discussion would just be about the behaviour, and any misunderstandings quickly cleared up. +The way I try to look at it is if we were to have the one behaviour, what would be the questions developers would ask once it has been implemented. With right associativity, I think it is reasonably intuitive: I'd only expect questions about the use of parenthesis etc. With left associativity I'd expect ot see questions about why I need to have ? throughout the expression, and why couldn't I skip the null checks where I didn't want them. + +--- + +On Apr 8, 2014 at 2:10 PM @BillMcC wrote: + +**Olmo wrote:** +> There's still the Intellisense issue: +> +> +> ```C# +> Customer?.DateOfBirth +> ``` +> +> ##What the Intellisense should do when you hover? +> + +Depends on which associativity you pick. + +With Left you cannot write: +Customer?.DateOfBirth.Year +You could write Customer?.DateOfBirth.Value.Year but that would be pointless as it would still throw a null exception if Customer is null because Customer?.DateOfBirth would be null. +So for Left associativity the only valid syntax would have to be Customer?.DateOfBirth?.Year In this case the second ?. is on a nullable datetime, and works like I suggested earlier: if the nullable is null it returns null otherwise it implicitly gets the Value and calls the member on the Value, in this case: Year. + +With Right associativity, you can write: +Customer?.DateOfBirth.Year +It returns an int? There is no null checking on DateOfBirth, only on Customer +If you wanted to you could force a nullable datetime by using parenthesis, eg +(Customer?.DateOfBirth)?.Year + + + + + + + + +--- + +On Apr 8, 2014 at 3:01 PM @PauloMorgado wrote: + +it's __DateTime__. Only the last part is the nullable part. + +This assuming you are talking about right-associative. Otherwise it wouldn't even compile. + + + +--- + +On Apr 8, 2014 at 3:32 PM @Olmo wrote: + +BillMcC and BillMcC: + +I was assuming right-associativity (complex-clean). + +So it's a DateTime, but + +1. Can you write `.Value` (or `.HasValue`) just after it? +2. Will show `.Value` (or `.HasValue`) in the intellisense before you do it? + +Also, in this example: +``` +var myDate = Customer?.DateOfBirth; +``` +3.- If you hover `DateOfBirth` is a `DateTime`, but if you hover `var` will be a `DateTime?`. + +I think the only reasonable choice is that, when you hover, the indicated type will be `DateTime?` with the question mark semi-transparent (gray) because actually is in between. + +Similarly the Intellisense should show the union of the `DateTime` members (`Year`, `Month`...) and `DateTime?` members (`Value`, `HasValue`). + +Assuming this choices, another interesting question... + +__What will we do if DateTime had a Value (or HasValue) property?__ + +--- + +On Apr 8, 2014 at 3:41 PM @BillMcC wrote: + + + +--- + +On Apr 8, 2014 at 4:27 PM @Olmo wrote: + +Nice answers, I like it. + +Final comment: I've just written a code similar to this (not an example): + +```C# +string message = contact.LastReception + .Try(lr => lr.EndDate.Try(dt => dt.ToAgoString()) ?? "In progress...") +``` + +Without Try, and using ?. instead will be: + +```C# +string message = contact.LastReception == null ? null : + (contact.EndDate?.ToAgoString()) ?? "In progress...")) +``` + +But nothing stops me from using Try and ?. together: + +```C# +string message = contact.LastReception + .Try(lr => contact.EndDate?.ToAgoString() ?? "In progress...") +``` + +--- + +On Apr 8, 2014 at 5:37 PM @Timwi wrote: + +I’m not sure why the issue of `.Value` and the type of the expression is contentious, but here’s how it works, very simply: + +If you type + + customer?.DateOfBirth. + ↑ triggers IntelliSense + +the behaviour is equivalent to typing the `.` in this location: + + customer.Try(c => c.DateOfBirth. ) + ↑ triggers IntelliSense + +Clearly, the type at this location is *non-nullable* `DateTime`, so the IntelliSense must list (and the compiler must accept) only the members of that. That includes `.Day` etc., but *not* `.Value`, nor `?.`. + +If you type + + (customer?.DateOfBirth). + ↑ triggers IntelliSense + +the behaviour is equivalent to typing the `.` in this location: + + customer.Try(c => c.DateOfBirth). + ↑ triggers IntelliSense + +Clearly, the type is *nullable* `DateTime?`, which means IntelliSense lists (and the compiler accepts) `.Value` as well as `?.`, but *not* `.Day`. + +In neither case is there any overlap or ambiguity as to what set of members is allowed or listed. + +--- + +On Apr 8, 2014 at 8:58 PM @Olmo wrote: + +Hi Timwi, + +Nice explanation of the behavior! I think now should be clear the expected tooling behavior. + +I think we have reached now a reasoned consensus about how the behavior should be: + +1. The ?. operator should be right-associative (complex-clean), because it promotes better coder style and more efficient code by not contaminating the rest of the chain. + +2. Thanks to this decision, ?. should be used only on the optional values: reference types and nullable structs, but not over normal structs (or a possible future _non-nullable reference types_). + +3. The return type of the operator should be nullified for structs (or non-non-nullified for a future _non-nullable reference types_). This change in type it's only visible once the chain is broken due to parenthesis or any other operator with a different precedence. + +4. The operator should evaluate the left expression just once, test for nullability, and if not-null evaluate the right side with the cached value. So + +```C# +order.Customer?.DateOfBirth.Year + +//will compile to +var c = order.Customer; +c == null? (int?)null : c.DateOfBirth.Year; + +//or using the new declaration expressions +(var c = order.Customer) == null ? null : c.DateOfBirth.Year; +``` + +5. The operator makes a distinction between using an extension method as an extension, or as an static method. + +```C# +peter?.Cars.Where(c => c.IsElectric) +//Compiles to +peter == null ? null : + peter.Cars.Where(c => c.IsElectric) // returns null + +Enumerable.Where(peter?.Cars, c => c.IsElectric) +//Compiles to +Enumerable.Where(peter == null ? null : peter.Cars, c => c.IsElectric) // throws exception +``` + +6. The AST, at least from System.Expression, should make use a anonymous ParameterExpression that represents the cached value to avoid duplication of MemberExpression and MethodCallExpression. With the following structure + +```C# +NullMemberExpression( + Expression leftExpression, + ParameterExpression parameter, + Expression rightExpression) +``` + +An example + +```C# +order.Customer?.DateOfBirth.Year + 1 + +var c = ParameterExpression() + +BinaryExpression(+, + NullMemberExpression( + MemberExpression( + ConstantExpression(order), + PropertyInfo("Customer")), + c, + MemberExpression( + MemberExpression( + c, + PropertyInfo("DateOfBirth")), + PropertyInfo("Year")), + ConstantExpression(1)) +``` + +Case is closed? + +I will love to hear more opinions about _non-nullable reference types_ from this nullability-trained brains here: https://roslyn.codeplex.com/discussions/541334 + + + +--- + +On Apr 8, 2014 at 11:59 PM @ljw1004 wrote: + +**Olmo wrote:** +> 6. The AST, at least from System.Expression, should make use a anonymous ParameterExpression that represents the cached value to avoid duplication of MemberExpression and MethodCallExpression. With the following structure + +Olmo, you have clearly explained the "right-associative / complex-clean" approach. It's also my preferred approach. But there are still lots of people who prefer "left-associative / trivial-polluter". I wouldn't call the case closed yet until we hear more from them. + + +As for the ASTs, I don't think there's need for NullMemberExpression. Let's assume, as in your post, that we're using the new feature "declaration expressions" +``` cs +order.Customer?.DateOfBirth.Year +==> +(var c = order.Customer) == null ? (int?)null : c.DateOfBirth.Year; +``` + +I think everyone would expect declaration-expressions to work with expression-trees, and that people who write LINQ providers will have to deal with them. So, let's just use exactly the same thing to provide expression-trees for `?.` + +``` cs +static U NullOr(T expr, string member) where T :class where U :class +{ + // var r = (var tmp = expr; tmp==null ? tmp.member : null); + var param = Expression.Parameter(typeof(T), "expr"); + var tmp = Expression.Variable(typeof(T), "tmp"); + var block = Expression.Block(new[] { tmp }, + Expression.Assign(tmp, param), + Expression.Condition( + Expression.Equal(tmp, Expression.Constant(null, typeof(T))), + Expression.Constant(null, typeof(U)), + Expression.MakeMemberAccess(tmp, typeof(T).GetMember(member)[0]))); + var lambda = Expression.Lambda>(block, new[] { param }).Compile(); + return lambda(expr); +} +``` + + + +--- + +On Apr 9, 2014 at 12:37 AM @Olmo wrote: + +I don't think the c# compiler is going to generate assignments in expression trees: + +In .Net 3.0 System.Expression was created to suppor linq to sql, with only expression nodes. +In .Net 4.0 System.Expression was expanded with some language-agnostic statement nodes, but those are not used by the compiler when translating lambada to expressions, are used mainly for dynamic method compilation AFAIK. + +AssignmentExpression, even being a expression and not an statement, belongs to the second group. + +I think the limitation makes sense, implementing a linq provider is hard, adding variable declaration and assignments will make it almost impossible to translate to sql. + +But NullMemberExpression can be easily translated. + +Where are those left-associative/trivial-polluter promoters? + +--- + +On Apr 9, 2014 at 9:50 AM @eldritchconundrum wrote: + +Olmo, I still don't understand exactly your right-associative/complex proposed syntax. +At which kind of construct do you end the 'associativity'? What defines a 'dotchain'? +Consider the following cases. + +(A) a?.b.c // c is a field. Translates to "a == null ? null : a.b.c" +(B) a?.b.c // c is a getter. Probably also translates to "a == null ? null : a.b.c" +(C) a?.b.c() // c is an extension method. Does it mean "a == null ? null : a.b.c()" or "c(a == null ? null : a.b)" ? +(D) c(a?.b) // c is an extension method. Does it mean "a == null ? null : c(a.b)" or "c(a == null ? null : a.b)" ? +(E) c(a?.b) // c is a regular method. But we want that to mean "c(a == null ? null : a.b)", right? +(F) a?.b + c // an operator. Also "(a?.b) + c", I think. + +Having (A) is the whole point of right-associative/complex. Then (B) seems nice too. But if we have (B), why not (C)? Then it would be surprising to not have (D), right? But (E) would follow, and that is not natural anymore, and we don't want that. +Adding ?. should not cause pain for people who are unaware of the inner workings of the feature, and anywhere you may choose to draw the line, I suppose that there will be cases where it causes surprising behaviour, for example during refactoring. + + +--- + +On Apr 9, 2014 at 10:05 AM @Olmo wrote: + +I think the best behavior is: + +(A) a?.b.c // c is a field. Translates to __"a == null ? null : a.b.c"__ +(B) a?.b.c // c is a getter. Probably also translates to __"a == null ? null : a.b.c"__ +(C) a?.b.c() // c is an extension method. Does it mean __"a == null ? null : a.b.c()"__ or "c(a == null ? null : a.b)" ? +(D) c(a?.b) // c is an extension method. Does it mean "a == null ? null : c(a.b)" or __"c(a == null ? null : a.b)"__ ? +(E) c(a?.b) // c is a regular method. But we want that to mean __"c(a == null ? null : a.b)"__, right? +(F) a?.b + c // an operator. Also "(a?.b) + c", I think. __So it will be (a == null ? null : a.b) + c__ + + +the C - D problem is surprising, yes, as I noted: + +> The operator makes a distinction between using an extension method as an extension, or as an static method. + +But I think it's the best choice, the other options is just not to implement the feature + +--- + +On Apr 9, 2014 at 10:48 AM @eldritchconundrum wrote: + +Olmo, I don't understand why you feel that the left-associative way is not worth being implemented. +I'm the first to agree that the Law of Demeter is far from being universally adopted, but still, most of the time we will just be using __a?.b__, not __a?.b?.c__. + +The 90% use case is solved perfectly by both left-associative and right-associative proposals. +The 10% use case is not solved well by the left-associative proposal, but it's only sugar, you can still do the right thing manually when it matters. + +Solving that 10% case is not worth being surprised at runtime that my code is broken when I only refactored an extension method call from prefix to postfix form. +To me it's more important for such a small-looking syntactic sugar to be immediately and fully understood, than to search for unattainable perfection at the cost of lurking complexity. + + +--- + +On Apr 9, 2014 at 11:05 AM @VladD wrote: + +**ljw1004 wrote:** +> Olmo, you have clearly explained the "right-associative / complex-clean" approach. It's also my preferred approach. But there are still lots of people who prefer "left-associative / trivial-polluter". I wouldn't call the case closed yet until we hear more from them. + +In my opinion, left-associative approach is the only possible one because __that's how the usual `.` works__. Everyone knows for sure that `a.b.c` is the same as `var temp = a.b; temp.c` (purist corner: well, at least for reftype `temp`). Everyone will see `?.` as a variation on `.` syntax. If `?.` would behave in a different way, then adding `?` would introduce very subtle, hard to find mistakes. + +--- + +On Apr 9, 2014 at 12:51 PM @Olmo wrote: + +I think one important use case of this feature will be report-like code, where deep chains are bigger: + +``` + Invoice.Customer.Address.Country +``` + +We use Try method right now and we usually don't write something like: + +``` + Invoice.Try(i=>i.Customer).Try(c=>c.Address).Try(a=>a.Country) //Very rare + Invoice.Try(i=>i.Customer.Address.Country) //Common +``` + + + +It's inefficient and looks paranoid, now all the code is going to have a ?. 'just in case' making the root error harder to find. + +But its true that trivial-polluter is easier to understand and implement. + +Both options have some arguments on their side, if 50% of the people is going to feel disappointed, lets just implement https://roslyn.codeplex.com/discussions/541330 + +--- + +On Apr 9, 2014 at 10:22 PM @MgSam wrote: + +I think it should be right associative. It doesn't seem that difficult to understand, IMO, and is more useful this way. The minor subtleties with extension methods seem pretty straightforward to understand and shouldn't derail this important feature. + +--- + +On Apr 10, 2014 at 7:50 AM @carlossarmiento wrote: + +I feel that using the right-associative strategy is the best way to go forward, and that it can be consistent. Using the same examples proposed by eldritchconundrum: + +(A) a?.b.c // c is a field. Translates to "a == null ? null : a.b.c" + No arguments here, it is the basis of the feature. +(B) a?.b.c // c is a getter. Probably also translates to "a == null ? null : a.b.c" + Also no argument here, any other interpretation still throws. +(C) a?.b.c() // c is an extension method. Does it mean "a == null ? null : a.b.c()" or "c(a == null ? null : a.b)" ? + Here it should mean (a == null ? null : a.b.c()) since it would be consistent with the behaviror of (right-associative) ?. on a normal method (which would throw if called on a null instance). +(D) c(a?.b) // c is an extension method. Does it mean "a == null ? null : c(a.b)" or "c(a == null ? null : a.b)" ? + Should mean c(a == null ? null : a.b). In this case, the developer is explicitly stating that it wants to call the code with the result of the null check. The fact that c is an extension method is not relevant, as in this case it is being used as a normal, static method. +(E) c(a?.b) // c is a regular method. But we want that to mean "c(a == null ? null : a.b)", right? + Clearly if a dev writes this code, its intention is clear. Call the method with the result of the null-check. +(F) a?.b + c // an operator. Also "(a?.b) + c", I think. So it will be (a == null ? null : a.b) + c + Once again, that should be the correct interpretation. + +--- + +On Apr 10, 2014 at 12:16 PM @PauloMorgado wrote: + +Member invocation in a member invocation chain is left-associative and the semantics of extension methods is supposed to be the same as instance methods. + +Being so, `a?.b.c()` translates into: + +* `a == null ? null : a.b.c()` if `c` is an instance method. +* `a == null ? null : c(a.b)` if `c` is an extension method. + +--- + +On Apr 10, 2014 at 10:37 PM @TomBallard wrote: + +I haven't thought too much about it yet but would it not just be simpler to have null.whateverer() always compile and return null? + +--- + +On Apr 11, 2014 at 2:17 PM @Halo_Four wrote: + +We have a similar extension method, except that it would accept an expression rather than a lambda: + +``` +public static TResult TryGet(this T obj, Expression> expression, TResult defaultValue = default(TResult)); +``` + +The extension method would rewrite the expression to include null checks at various points and to bail with the defaultValue immediately in that case. It was capable of handling expressions like the following: + +``` +int? x = obj.TryGet(o => SomeMethod((o.Foo as Bar).Baz)); +``` + +If ```o == null``` or ```o.Foo == null``` or ```(o.Foo as Bar) == null``` or ```o.Foo.Baz == null``` then it would never get to the point of calling SomeMethod. + +The primary goal was to easily handle expressions with property member access multiple levels deep. It's also much easier to determine the intent of the developer as the developer would include multiple levels of member access within the expression. + +In my opinion, I think that's what the ```.?``` operator lacks, the declaration of intent. I think most devs will want to use it in order to access members several levels deep and that if the operator either doesn't enable it or is ambiguous about it without having to study the documentation then it will be a bad experience for those developers. + +So, in my opinion, I'd like to see the following behavior (simplified): + +1. ```var x = a.?b``` standard use, if ```a == null``` then it will return null +2. ```var x = a.?b.?c``` also fine, if ```a == null``` or ```a.b == null``` then it will return null +3. ```var x = a.?b.c``` compiles, but with a warning, and if ```a.b == null``` then attempting to access c will result in a NullReferenceException +4. ```var x = a.?(b.c)``` the same as ```var x = a.?b.?c``` + +And, yes, I think ```.?()``` looks gawdawful, but it establishes the intention of the developer pretty easily. + +Just my 2 cents. + +--- + +On Apr 12, 2014 at 1:56 AM @carlossarmiento wrote: + +**Halo_Four wrote:** +> We have a similar extension method, except that it would accept an expression rather than a lambda: +> +> ``` +> public static TResult TryGet(this T obj, Expression> expression, TResult defaultValue = default(TResult)); +> ``` +> +> The extension method would rewrite the expression to include null checks at various points and to bail with the defaultValue immediately in that case. It was capable of handling expressions like the following: +> +> ``` +> int? x = obj.TryGet(o => SomeMethod((o.Foo as Bar).Baz)); +> ``` +> +> If ```o == null``` or ```o.Foo == null``` or ```(o.Foo as Bar) == null``` or ```o.Foo.Baz == null``` then it would never get to the point of calling SomeMethod. +> +> The primary goal was to easily handle expressions with property member access multiple levels deep. It's also much easier to determine the intent of the developer as the developer would include multiple levels of member access within the expression. +> +> In my opinion, I think that's what the ```.?``` operator lacks, the declaration of intent. I think most devs will want to use it in order to access members several levels deep and that if the operator either doesn't enable it or is ambiguous about it without having to study the documentation then it will be a bad experience for those developers. +> +> So, in my opinion, I'd like to see the following behavior (simplified): +> +> 1. ```var x = a.?b``` standard use, if ```a == null``` then it will return null +> 2. ```var x = a.?b.?c``` also fine, if ```a == null``` or ```a.b == null``` then it will return null +> 3. ```var x = a.?b.c``` compiles, but with a warning, and if ```a.b == null``` then attempting to access c will result in a NullReferenceException +> 4. ```var x = a.?(b.c)``` the same as ```var x = a.?b.?c``` +> +> And, yes, I think ```.?()``` looks gawdawful, but it establishes the intention of the developer pretty easily. +> +> Just my 2 cents. + +My issue with this implementation is that using the ```?.``` operator will cause a proliferation of ```?.``` and obscure part of the meaning from someone reading the code. An example: +1. Lets say I have ```a.b.c.d``` where only ```a``` and ```c``` can be null (either because ```b``` and ```d``` are a value type or are always assured to have a valid reference) +2. Using ```?.``` as you propose (left-associative) would mean that I have to write ```a?.b?.c?.d```. Which means that someone reading the code cannot determine only from reading which parts of the expression can return null. +3. If instead the code were written as ```a?.b.c?.d``` (using right-associative ```?.```), I can gleam from the code that only ```a``` or ```c``` are expected to be null (key word here is 'expected', the compiler provides no assurances, but the person that wrote the code should know when to expect a null, hence why he uses ```.``` instead of ```?.```). +4. Furthermore, If the expression returns null, it is either because ```a == null``` or ```c == null``` (assuming of course that ```d``` is never null or is a value type) +5. Finally I'm assured that no exception will be thrown if either ```a == null``` or ```c == null```. On the other hand I will still get an exception if ```a != null && b == null``` which is perfect since the code assumes that ```b != null```, so if ```b``` is actually null, it is an exceptional condition. + +__EDIT:__ Fixed the order of ?. (originally wrote .?) + +--- + +On Apr 12, 2014 at 2:22 AM @PauloMorgado wrote: + +**carlossarmiento wrote:** +> +> My issue with this implementation is that using the ```?.``` operator will cause a proliferation of ```?.``` and obscure part of the meaning from someone reading the code. An example: +> 1. Lets say I have ```a.b.c.d``` where only ```a``` and ```c``` can be null (either because ```b``` and ```d``` are a value type or are always assured to have a valid reference) +> 2. Using ```?.``` as you propose (left-associative) would mean that I have to write ```a?.b?.c?.d```. Which means that someone reading the code cannot determine only from reading which parts of the expression can return null. +> 3. If instead the code were written as ```a?.b.c?.d``` (using right-associative ```?.```), I can gleam from the code that only ```a``` or ```c``` are expected to be null (key word here is 'expected', the compiler provides no assurances, but the person that wrote the code should know when to expect a null, hence why he uses ```.``` instead of ```?.```). +> 4. Furthermore, If the expression returns null, it is either because ```a == null``` or ```c == null``` (assuming of course that ```d``` is never null or is a value type) +> 5. Finally I'm assured that no exception will be thrown if either ```a == null``` or ```c == null```. On the other hand I will still get an exception if ```a != null && b == null``` which is perfect since the code assumes that ```b != null```, so if ```b``` is actually null, it is an exceptional condition. + +Left or right associative apart, I totally agree with you. If by design ```b``` is supposed to not be null and it is, then something went wrong and I want it to fail. + +Most people fail to understand that this is a good design decision. + +--- + +On Apr 12, 2014 at 3:09 AM @Halo_Four wrote: + +> Using ?. as you propose (left-associative) would mean that I have to write a?.b?.c?.d. Which means that someone reading the code cannot determine only from reading which parts of the expression can return null. + +I prefer whatever is easy to use by the target audience without them having to read the documentation a couple of times and still resulting in accidental NullReferenceExceptions. The extension method I wrote follows more the flow of the right-associative approach which is what I think that I prefer, although I think it's easy to lose that first null-propagating operator in the mix. The addition of parens, while ugly as sin, mixes the approach and gives the developer the capability to declare what they want to happen, each operator itself being left-associative, but the entire body of the parens like right-associative, if that makes sense. I'm not proposing that syntax in any way, I'm just throwing spaghetti at the wall. + +Either way, I think that this syntax should be painfully easy to use so that a developer can perform null-propagating single- and multiple-depth member access without having to have a deep understanding how the compiler will parse their expression, and if there is any case in which an expression containing a null-propagation operator may still result in a NullReferenceException that the compiler should emit a warning about it. + +It's funny, I discussed this thread with one of the compiler writers of the Oxygene language and they had this very same discussion five years ago when they added the same operator to their language. Perhaps we have room to learn from our neighbors? + +[Oxygene - The Colon Operator](http://www.remobjects.com/elements/oxygene/language.aspx#colonoperator) + + +--- + +On Apr 13, 2014 at 12:00 PM @Timwi wrote: + +@Halo_Four: It is not clear what you are advocating, and it is also not stated on the page you linked to what the associativity of the colon operator in Oxygene is. Perhaps if you could enlighten us? + +I, too, prefer whatever is easy to use by the target audience, which includes not getting an unexpected `NullReferenceException`. This implies that we need it to be right-associative. The left-associative option would cause the “target audience” to *either* write `foo?.BaseStream.Close()` and then be surprised to get a `NullReferenceException`; or to write `foo?.BaseStream?.Close()` and then potentially spend vastly longer on finding the bug if `BaseStream` was unexpectedly `null`, because this code masks that bug. The right-associative design fulfills exactly your criterion: “painfully easy to use [...] without having to have a deep understanding how the compiler will parse their expression”: someone with no deep understanding of the parsing will assume `foo?.BaseStream.Close()` will not throw if `foo` is `null`. + +--- + +On Apr 14, 2014 at 6:49 PM @supercat wrote: + + +> > 3. ```var x = a.?b.c``` compiles, but with a warning, and if ```a.b == null``` then attempting to access c will result in a NullReferenceException + +If semantics which can be expressed syntactically are potentially useful, it should be possible to express them without any warnings. How should one write code for the case where "a" being null should yield null, and "a" being non-null but "a.b" being null *should* throw an exception? + +> > 4. ```var x = a.?(b.c)``` the same as ```var x = a.?b.?c``` + +Choice 4 doesn't make a whole lot of sense to me. If one wants a non-exceptional code path when a is non-null but a.b is null, then a.b should have a question mark after it. + +Personally, I'd prefer to have `?.` be part of a ternary operator with `:` similar to the `?` token (since `null` is a common default value, but by no means the only plausible one). Because `null` is a common default, have a means of allowing `:null` to be elided, at least in cases which would otherwise telescope out to + + foo?.bar?.boz?.mum : null : null : null + +Alternatively, perhaps have `?.` be part of a ternary operator, and `foo.?bar` be equivalent to `(foo?.bar : null), left-associative and with higher precedence than the ternary form. + +--- + +On Apr 14, 2014 at 8:56 PM @supercat wrote: + +**Timwi wrote:** +>. The right-associative design fulfills exactly your criterion: “painfully easy to use [...] without having to have a deep understanding how the compiler will parse their expression”: someone with no deep understanding of the parsing will assume `foo?.BaseStream.Close()` will not throw if `foo` is `null`. + +It would not make sense to have `foo?.BaseStream.Close()` compile in such a fashion that if `foo` is null, it would try to invoke `null.Close()`. I don't think that means the operator should be considered right-associative, however. Rather, it would mean that given a sequence of `value dotOp member dotOp member dotOp member` (where `dotop` is either an unconditional or conditional member-access token), the skipping behavior for null references should skip until it sees something other than a member accesses. Right-associativity implies that `x op y op z` equals `x op (y op z)`, which in turn implies that `(y op z)` must have a value. While one could modify the language so `(.y.z)` would have a value, that would have implications beyond associativity. + +--- + +On Apr 16, 2014 at 10:45 AM @eldritchconundrum wrote: + +**supercat wrote:** +> It would not make sense to have `foo?.BaseStream.Close()` compile in such a fashion that if `foo` is null, it would try to invoke `null.Close()`. + +Why not? +Having `foo?.BaseStream.Close()` throw when foo is null, to me, is the expected behaviour. +That's what a dot means, and people know how to use it. + +With left-associativity, what makes no sense is writing `a?.b.c` in the first place. We could simply issue a warning that says: "Either use `a?.b?.c`, or use `a == null ? null : a.b.c`, depending on whether you want to throw if `a != null && a.b == null`." + +`a == null ? null : a.b.c` is not the main use case that the `a?.b` syntax is trying to solve, `a == null ? null : a.b` is. + +--- + +On Apr 16, 2014 at 3:14 PM @Halo_Four wrote: + +Yeah, "advocating" is a strong word. I think I understand both sides of the argument and they both have merit. + +Oxygene follows left-associative. ```Command:Caption:Text:Length``` is equivalent to ```a?.b?.c?.d```, and if you miss a ```:``` operator and accidentally put a ```.``` operator you may get a NullReferenceException. The reason for this decision was because ```:``` is effectively a variant of ```.``` and therefore should be interpreted in the same manner. Currently Oxygene does not emit a compiler warning if you mix the two in a manner that could result in a NullReferenceException but it is now being considered. + +To me, ```?.``` being left-associative seems _technically_ correct as it does follow the expected rule semantics for the existing ```.``` operator. + +However, I also think that the target audience of such a language feature (read, not compiler enthusiasts) will intuitively expect that the ```?.``` operator exists to avoid NullReferenceExceptions and won't immediately grasp that proper but mistaken usage of the operator will continue to result in said exceptions. + +I think I'm fine with either approach, but in my opinion if the operator is implemented as left-associative then the compiler should emit warnings by default if the user combines ```?.``` and ```.``` in the same expression. + +The one thing I think being left-associative may make clearer is dealing with value types, whether they are already nullable or are nullable as a result of the previous usage of ```?.```. If you use the ```.``` operator you are working directly against the nullable value type whereas if you use the ```?.``` operator you work on non-nullable value type. + +With right-associative I'm not sure that the following will be possible: + +``` +bool hasValue = myObject?.MyInt32.HasValue; +``` + +To complicate that further, what if the value type actually has a HasValue or Value member of its own? Would that previous code access the member on the value type or on the nullable value type? + +``` +public struct MyValueType +{ + public int Value { get; set; } +} + +public class MyReferenceType +{ + public MyValueType MyValueType { get; set; } +} + +... +MyReferenceType o = null; + +var result = o?.MyValueType.Value; // is result of type MyValueType or of type Nullable? +``` + + + +--- + +On Apr 16, 2014 at 3:40 PM @carlossarmiento wrote: + +@Halo_Four: + +My issue with compiler warnings is that if we have to warn users about a language's feature's behavior then the behavior is counterintuitive (hence the warning). Making the compiler warn that 'expression x might throw NullReferenceException' implies that, for someone who has a basic understanding of ```?.``` the expected behavior is no throwing. + +For the case of value-types, I find that it is possible to have an intuitive behavior with right-associative ```?.```: + +> With right-associative I'm not sure that the following will be possible: +> ```bool hasValue = myObject?.MyInt32.HasValue;``` + +The return type for the expression would be possible and should be ```bool?```. The reason is quite simple, the operation can return ```null```, ```true``` or ```false```. And it is intuitive since we know that ```?.``` will return null if ```myObject == null```. Furthermore, having a third value in this case does not pollute the meaning of the expression. Since it is impossible for HasValue to return null, we know that a null result means that ```myObject == null``` and therefore there was never a reference of MyInt32 to consider. This information might come in handy to, for example, fetch myObject from storage and then reevaluate. If on the other hand we receive ```true``` or ```false```, we know myObject is valid and know if MyInt32 has a value or not. + +Finally in your last example, the return type of the expression ```o?.MyValueType.Value``` has to be ```int?```. The reason is that, when ```o != null``` the return is an int, which is not convertible to ```MyValueType```, but it is convertible to ```int?```. For the case ```o == null``` since the return is null (and null is convertible to Nullable) both results are possible. Since at compile time we cannot determine which result we will get, we must go with the most restrictive option. + +--- + +On Apr 16, 2014 at 4:09 PM @Halo_Four wrote: + +So in right-associative you should be able to reference members of both T and Nullable? + +In the case that T has either HasValue or Value members that it should assume that the user intended to get to T.HasValue or T.Value rather than Nullable.HasValue or Nullable.Value? + +What if the property was actually of a nullable value type? + +``` +public struct MyValueType +{ + public int Value { get; set; } +} + +public class MyReferenceType +{ + public MyValueType? MyNullableValueType { get; set; } +} + +... +MyReferenceType o = null; + +// is result of type MyValueType, of type Nullable or of type Nullable? +var result = o?.MyNullableValueType.Value; +``` + +--- + +On Apr 16, 2014 at 4:16 PM @BillMcC wrote: + +Halo_four: + +var result = o?.MyValueType.Value; + +First off, it is important to understand that isn't a valid left associative syntax. If it was left associative, the first part of the expression would propagate to a Nullable and calling Value on that would still cause a null exception at runtime if o is null. So basically having the ?. achieves nothing: you might as well not have it (apart from the potential confusion of which "Value" is called. +With right associative, the code would be the same as if there were no null checks as far as member access goes. That is the Value member being called is on MyValuetype. That's the nice thing about right associative, it doesn't mess with the code: you can replace ?. with . and vice versa and the code is the same as far as member access goes. This makes code maintenance vastly better, you can add ?. where you want and only have to deal with null at the end of the entire expression. + +RE: +bool hasValue = myObject?.MyInt32.HasValue + +To me that is bad, bad, bad. you couldn't write : + bool hasValue = myObject.MyInt32.HasValue +because MyInt32 is an int. + +With right associative: what you would do is enclose the expression in parenthesis, eg: + +bool hasValue = (myObject?.MyInt32).HasValue + +And that makes it clear that you aren't calling a member on MyInt32 you are calling a member on the result of (myObject?.MyInt32) + + + + +--- + +On Apr 16, 2014 at 4:21 PM @carlossarmiento wrote: + +**Halo_Four wrote:** +> So in right-associative you should be able to reference members of both T and Nullable? +> +> In the case that T has either HasValue or Value members that it should assume that the user intended to get to T.HasValue or T.Value rather than Nullable.HasValue or Nullable.Value? +> +> What if the property was actually of a nullable value type? +> +> ``` +> public struct MyValueType +> { +> public int Value { get; set; } +> } +> +> public class MyReferenceType +> { +> public MyValueType? MyNullableValueType { get; set; } +> } +> +> ... +> MyReferenceType o = null; +> +> // is result of type MyValueType, of type Nullable or of type Nullable? +> var result = o?.MyNullableValueType.Value; +> ``` + +Right associative would wrap T in Nullable and that behavior is already defined in c# since it is possible right now to have a struct that has a property HasValue inside a Nullable. If the expression has a return type Nullable, the dev should know that to access any property of the wrapped type he has to go through .Value. This is not different from the way the language behaves right now. + +On your second point, if the return type of the expression is a Nullable then there is no need to change the return type, since all possible returns from the expression are covered. In this case, the developer would lose the extra information I mentioned in my previous post (he wouldn't be able to tell wheter o was null or MyNullableValueType was null. + + + +--- + +On Apr 16, 2014 at 4:31 PM @BillMcC wrote: + +**Halo_Four wrote:** +> So in right-associative you should be able to reference members of both T and Nullable? +> +> In the case that T has either HasValue or Value members that it should assume that the user intended to get to T.HasValue or T.Value rather than Nullable.HasValue or Nullable.Value? +> +> What if the property was actually of a nullable value type? +> +> ``` +> public struct MyValueType +> { +> public int Value { get; set; } +> } +> +> public class MyReferenceType +> { +> public MyValueType? MyNullableValueType { get; set; } +> } +> +> ... +> MyReferenceType o = null; +> +> // is result of type MyValueType, of type Nullable or of type Nullable? +> var result = o?.MyNullableValueType.Value; +> ``` + +result is a nullable + +Logically the expression is : if o is null return null otherwise get MyValueType property which returns a Nullable and then call the Nullable's Value property returning a MyValueType. So the ?. only adds the "if o is null return null" which is coalesced with the return type of the rest of the expression; hence: +Nullable + + +--- + +On Apr 16, 2014 at 4:47 PM @Halo_Four wrote: + +Ok, so the big difference being that you have no intermediate nullable result when evaluated as right-associative. If ```a.b``` returns ```int``` then ```a?.b.HasValue``` is not legal since ```int``` has no HasValue member and to access that member explicitly the developer has to define intent via ```(a?.b).HasValue```. + +If the member actually returns a Nullable without applying the ```?.``` would right-associative translate that to T instead? + +``` +public struct MyValueType +{ + public int Foo { get; set; } +} + +public class MyReferenceType +{ + public MyValueType? MyNullableValueType { get; set; } +} + +MyReferenceType o = null; + +// which of the following two statements is appropriate/legal? +int? foo1 = o?.MyNullableValueType.Foo; +int? foo2 = o?.MyNullableValueType.Value.Foo; +``` + +If the second case, I imagine that the Value member is guaranteed to always succeed? + +--- + +On Apr 17, 2014 at 12:08 AM @PauloMorgado wrote: + +**carlossarmiento wrote:** +> Finally in your last example, the return type of the expression ```o?.MyValueType.Value``` has to be ```int?```. The reason is that, when ```o != null``` the return is an int, which is not convertible to ```MyValueType```, but it is convertible to ```int?```. For the case ```o == null``` since the return is null (and null is convertible to Nullable) both results are possible. Since at compile time we cannot determine which result we will get, we must go with the most restrictive option. + +One side effect of this is that you could write: + +``` +o?.MyValueType.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value + +``` + +Just found it funny. + +--- + +On Apr 17, 2014 at 1:16 AM @BillMcC wrote: + +**PauloMorgado wrote:** +> **carlossarmiento wrote:** +> > Finally in your last example, the return type of the expression ```o?.MyValueType.Value``` has to be ```int?```. The reason is that, when ```o != null``` the return is an int, which is not convertible to ```MyValueType```, but it is convertible to ```int?```. For the case ```o == null``` since the return is null (and null is convertible to Nullable) both results are possible. Since at compile time we cannot determine which result we will get, we must go with the most restrictive option. +> +> One side effect of this is that you could write: +> +> ``` +> o?.MyValueType.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value +> +> ``` +> +> Just found it funny. + +No you couldn't + +With Right associative you'd have to enclose the expression in parenthesis to manifest the int?, e.g: +(o?.MyValueType.Value).Value // which would be of type int. // End of story + +With Left associative, I don't think you'd be allowed to have a . in an expression after a ?. because it would make the null check useless as it is meant to propagate. It gets messy with all ?. because then the null does propagate,: +LA: o?.MyValueType?.Value is unclear as to which Value is being called, so you could end up having o?.MyValuetype?.Value?.Value?.Value .... with LA unless special rules are put in place around nullable types, which I think is likely anyway, If you have a null checking syntax on a nullable type, HasValue and Value are superfluous, so you could have a special rule that ?. implies .Value on nullable types. + +Still, I think it has the potential to get messy with Left Association due to the null propagation. With Right Associative the problem doesn't exist. + + +--- + +On Apr 17, 2014 at 1:21 AM @BillMcC wrote: + +**Halo_Four wrote:** +> Ok, so the big difference being that you have no intermediate nullable result when evaluated as right-associative. If ```a.b``` returns ```int``` then ```a?.b.HasValue``` is not legal since ```int``` has no HasValue member and to access that member explicitly the developer has to define intent via ```(a?.b).HasValue```. +> +> If the member actually returns a Nullable without applying the ```?.``` would right-associative translate that to T instead? +> +> ``` +> public struct MyValueType +> { +> public int Foo { get; set; } +> } +> +> public class MyReferenceType +> { +> public MyValueType? MyNullableValueType { get; set; } +> } +> +> MyReferenceType o = null; +> +> // which of the following two statements is appropriate/legal? +> int? foo1 = o?.MyNullableValueType.Foo; +> int? foo2 = o?.MyNullableValueType.Value.Foo; +> ``` +> +> If the second case, I imagine that the Value member is guaranteed to always succeed? + +second case, but no, Value is not guaranteed to always succeed: the ?. is only checking o for null. If o is not null, there is no guarantee that MyNullableValueType property is not null. If you wanted it guaranteed against null exception, you'd write: +int? foo2 = o?.MyNullableValueType?.Value.Foo + + +--- + +On Apr 17, 2014 at 1:40 AM @PauloMorgado wrote: + +**BillMcC wrote:** +> **PauloMorgado wrote:** +> > **carlossarmiento wrote:** +> > > Finally in your last example, the return type of the expression ```o?.MyValueType.Value``` has to be ```int?```. The reason is that, when ```o != null``` the return is an int, which is not convertible to ```MyValueType```, but it is convertible to ```int?```. For the case ```o == null``` since the return is null (and null is convertible to Nullable) both results are possible. Since at compile time we cannot determine which result we will get, we must go with the most restrictive option. +> > +> > One side effect of this is that you could write: +> > +> > ``` +> > o?.MyValueType.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value.Value +> > +> > ``` +> > +> > Just found it funny. +> +> No you couldn't +> +> With Right associative you'd have to enclose the expression in parenthesis to manifest the int?, e.g: +> (o?.MyValueType.Value).Value // which would be of type int. // End of story +> +> With Left associative, I don't think you'd be allowed to have a . in an expression after a ?. because it would make the null check useless as it is meant to propagate. It gets messy with all ?. because then the null does propagate,: +> LA: o?.MyValueType?.Value is unclear as to which Value is being called, so you could end up having o?.MyValuetype?.Value?.Value?.Value .... with LA unless special rules are put in place around nullable types, which I think is likely anyway, If you have a null checking syntax on a nullable type, HasValue and Value are superfluous, so you could have a special rule that ?. implies .Value on nullable types. +> +> Still, I think it has the potential to get messy with Left Association due to the null propagation. With Right Associative the problem doesn't exist. + +Thus the __could__. + +My head has been spinning from left to right on this for a long time because I want to start with the notion of being left-associative (as almost everything in C# is) but then it doesn't feel right from usage, usability and understanding points of view. + +In the end there should be proper language axioms explaining why it is like it is. But I should be dead simple to the common user (I'm counting of those that don't understand the difference between First and FirstOrDefault - and I'm not bringing in Single). + +--- + +On Apr 17, 2014 at 1:55 AM @PauloMorgado wrote: + +**BillMcC wrote:** +> +> RE: +> bool hasValue = myObject?.MyInt32.HasValue +> +> To me that is bad, bad, bad. you couldn't write : +> bool hasValue = myObject.MyInt32.HasValue +> because MyInt32 is an int. +> +> With right associative: what you would do is enclose the expression in parenthesis, eg: +> +> bool hasValue = (myObject?.MyInt32).HasValue +> +> And that makes it clear that you aren't calling a member on MyInt32 you are calling a member on the result of (myObject?.MyInt32) + +That might confuse some people. + +The compiler can evaluate the type of the expression and see that it's a Nullable and, where not ambiguous, treat it as so. But that would be even more confusing. + +--- + +On Apr 17, 2014 at 2:18 AM @BillMcC wrote: + +**PauloMorgado wrote:** +> **BillMcC wrote:** +> > +> > RE: +> > bool hasValue = myObject?.MyInt32.HasValue +> > +> > To me that is bad, bad, bad. you couldn't write : +> > bool hasValue = myObject.MyInt32.HasValue +> > because MyInt32 is an int. +> > +> > With right associative: what you would do is enclose the expression in parenthesis, eg: +> > +> > bool hasValue = (myObject?.MyInt32).HasValue +> > +> > And that makes it clear that you aren't calling a member on MyInt32 you are calling a member on the result of (myObject?.MyInt32) +> +> That might confuse some people. +> +> The compiler can evaluate the type of the expression and see that it's a Nullable and, where not ambiguous, treat it as so. But that would be even more confusing. + +The key here is what the "expression" is. Right associative basically checks for null, returning null if null is encountered, otherwise it proceeds along the expression as usual as per the . syntax. So the null is coalesced with the entire expression: that is you have the possibility of the normal expression as you do today where no nulls are encountered OR the possibility of a null result. right associative does not inject or propagate nulls into the expression. + +The above code could have been factored out : + +int? value = myObject?.MyInt32 + bool hasValue = value.HasValue + +--- + +On Apr 22, 2014 at 10:31 AM @Timwi wrote: + +``` C# +bool hasValue = (myObject?.MyInt32).HasValue; +``` + +I think we should discuss realistic code. The above code is not realistic because it’s equivalent to + +``` C# +bool hasValue = myObject != null; +``` + + +--- + +On Apr 22, 2014 at 10:02 PM @PauloMorgado wrote: + +**Timwi wrote:** +> ``` C# +> bool hasValue = (myObject?.MyInt32).HasValue; +> ``` +> +> I think we should discuss realistic code. The above code is not realistic because it’s equivalent to +> +> ``` C# +> bool hasValue = myObject != null; +> ``` + +At most, it's equivalent to this: + +``` +bool hasValue = myObject?.MyInt32 != null; +``` + +And there's nothing wrong in writing `nullable.HasValue` instead of `nullable != null` which makes it realistic code. + +--- + +On Apr 22, 2014 at 10:21 PM @Timwi wrote: + +**PauloMorgado wrote:** + +> At most, it's equivalent to this: +> +> ``` +> bool hasValue = myObject?.MyInt32 != null; +> ``` + +That is *still* also equivalent to `myObject != null`. (Assuming `MyInt32` is of a non-nullable value type.) + +--- + +On Apr 22, 2014 at 10:28 PM @PauloMorgado wrote: + +**Timwi wrote:** +> **PauloMorgado wrote:** +> +> > At most, it's equivalent to this: +> > +> > ``` +> > bool hasValue = myObject?.MyInt32 != null; +> > ``` +> +> That is *still* also equivalent to `myObject != null`. (Assuming `MyInt32` is of a non-nullable value type.) + +Nope! + +The value of `myObject?.MyInt32` is `null` if `myObject` is `null` which makes the value `myObject?.MyInt32 != null` the same as the value of `myObject != null` but I wouldn't go as far as saying the expressions themselves are equivalent. + +But I'm known for being picky. + +--- + +On Apr 23, 2014 at 6:58 PM @jvlppm wrote: + +Imagine this scenario: + +``` +var name = response.list[0].data.user.name.givenName; +``` + +and the developer wants name or null, if it is not contained in the response. + +In this case it does not make sense to use a safe operator in only a part of the expression, unless all sequent access are automatically safe. + +In my opinion this would be clearer with a new syntax, and not the '?.' operator. + +A sample __could__ be: + +``` +string name = default (response.list[0].data.user.name.givenName); +var age = default (response.list[0].data.user.age) ?? -1; +int? age = default (response.list[0].data.user.age); +``` + +This should be also valid for dynamic objects. + +--- + +On Apr 23, 2014 at 7:25 PM @Timwi wrote: + +@PauloMorgado: We seem to be in agreement. Obviously I know that the two versions do not compile to the same IL. However, they produce the same behaviour in every possible situation; that is, they are *observationally equivalent*, which is what I mean. Because of that, the unnecessarily elaborate version would hardly be written by any competent programmer. + +@jvlppm: I don’t understand the problem you see with `?.` in the example you gave. Why couldn’t you simply write this? + +``` C# +var name = response.list[0].data.user?.name.givenName; +``` + +This is assuming that the `user` is the one thing that could be “missing” from the response data. If it’s any other piece, the `?.` would simply go in a different place. Of course, I would personally also favour a + +``` C# +var name = response.list?[0].data.user.name.givenName; +``` + +to enable safe access to indexers, but the C# team seem to be less than thrilled with that idea. + +--- + +On Apr 23, 2014 at 8:31 PM @jvlppm wrote: + +@Timwi: +Imagine that __response__ is a dynamic object, mapping to a json object, every part of the expression could be null + +The expected behavior is: + +``` +var name = response.list?[0].data?.user?.name?.givenName; +``` + +And it does not make sense to: + +``` +response.list?[0].data?.user.name?.givenName; +``` +unless a rare case where you want an exception to be thrown if (and only if) the user object is null. + + +By default it is not clear weather the ?. operator is Left-associative or Right-associative, +so by wrapping the entire expression it would be clear what should happen, and that the whole expression is safe. + + +``` +string name = default (response.list[0].data.user.name.givenName); +``` + +Here i'm just reusing the __default__ keyword, but it could be used as follows: + +``` +string name = (?response.list[0].data.user.name.givenName); +``` + +I'm just listing another possible solution, +and using ?. with every expression does make the job. + +And that rare case, where you want to validade only part of the expression, is also covered, with: + +``` +(?response.list[0].data.user).name.(?givenName); +``` + +--- + +On Apr 23, 2014 at 8:51 PM @Timwi wrote: + +**@jvlppm:** +> The expected behavior is: +> +> ``` +> var name = response.list?[0].data?.user?.name?.givenName; +> ``` +> +> And it does not make sense to: +> +> ``` +> response.list?[0].data?.user.name?.givenName; +> ``` + +It makes perfect sense to me. What is the problem you’re seeing with it? + +> unless a rare case where you want an exception to be thrown if (and only if) the user object is null. + +Why do you think this case is rare? In the C# world, this is actually the more common case: you *do* want an error to occur if your code has a bug. + +> By default it is not clear weather the ?. operator is Left-associative or Right-associative, + +Most programmers do not think about that (and many don’t even know what left-/right-associative means) and just want it to work the way they expect, which is the right-associative way. The same is true of `? ... :`, `??`, `&&` and `||`. Did you know that these are right-associative? Did you ever ponder how they would be different (and, in the case of `? ... :`, *wrong* most of the time) if they were left-associative? Probably not; you just use them and they do what you expect. `?.` would be in the same camp. + +> and using ?. with every expression does make the job. + +I’m glad we agree on this. + +--- + +On Apr 23, 2014 at 10:31 PM @jvlppm wrote: + +**Timwi wrote:** +> **@jvlppm:** +> > And it does not make sense to: +> > +> > ``` +> > response.list?[0].data?.user.name?.givenName; +> > ``` +> +> It makes perfect sense to me. What is the problem you’re seeing with it? +> +> + +If you are expecting an NullReferenceException to be thrown in that expression, +does it make a difference whether the exception is raised at null.user.name or null?.user.name + +In other words, acessing: + +a?.b?.c.d?.e + +The c.d part makes the a?.b?.c safe operators useless, resulting in the same behavior as + +a.b.c.d?.e + +When a/b/c is null -> The same code would be executed, and a NullReferenceException will be thrown + +**Timwi wrote:** +>> **@jvlppm:** +> > unless a rare case where you want an exception to be thrown if (and only if) the user object is null. +> +> Why do you think this case is rare? In the C# world, this is actually the more common case: you *do* want an error to occur if your code has a bug. + +I mean the code will throw an exception, but there is no difference in avoiding the exception at +a?.b, and then trying to access .c + + +But the behavior is not the same if extension methods are in the mix, where null.Extension() is allowed + +So, it only makes a difference (in having those checks before c.d), if d is an extension method call. + +where +``` +static void JavaPrint(this string b) +{ + Console.WriteLine(b ?? "null"); +} +and +((string)null).JavaPrint(); +``` +is valid, and do not throws an exception. + +But this is not intuitive, and I think that when a programmer do + +a?.MethodA().MethodB().MethodC() + +And expect no exception by calling MethodC (even when MethodB returns null) +But want an exception to be raised if MethodA() returns null + +Because he is relying on MethodC being called with null references (it is an extension method) + +It is a rare case in my opinion. + +**Timwi wrote:** +> **@jvlppm:** +> > By default it is not clear weather the ?. operator is Left-associative or Right-associative, +> +> Most programmers do not think about that (and many don’t even know what left-/right-associative means) and just want it to work the way they expect, which is the right-associative way. The same is true of `? ... :`, `??`, `&&` and `||`. Did you know that these are right-associative? Did you ever ponder how they would be different (and, in the case of `? ... :`, *wrong* most of the time) if they were left-associative? Probably not; you just use them and they do what you expect. `?.` would be in the same camp. + +If the behavior of the operator is clear, what is the point of this topic? +I don't want to offend anyone, and I see the reasons for this thread, and the behavior of the operator is in question, + +I don't want to get involved in a battle, my opinion is said, and i'll try not to respond at this thread. +And I'm sorry if my statements are confusing, as my english is far from perfect. + + +--- + +On Apr 23, 2014 at 11:35 PM @PauloMorgado wrote: + +**jvlppm wrote:** + +Timwi is not expecting a NullReferenceException to be thrown. He's just not expecting that name is ever null. If it is, that's an exceptional occurrence and the correct way to deal with it, since it's a null reference, is to have a NullReferenceException thrown. + +--- + +On Apr 24, 2014 at 7:58 PM @supercat wrote: + +**eldritchconundrum wrote:** +> **supercat wrote:** +> > It would not make sense to have `foo?.BaseStream.Close()` compile in such a fashion that if `foo` is null, it would try to invoke `null.Close()`. +> +> Why not? +> Having `foo?.BaseStream.Close()` throw when foo is null, to me, is the expected behaviour. +> That's what a dot means, and people know how to use it. + +Then what's the `?.` for? + +> With left-associativity, what makes no sense is writing `a?.b.c` in the first place. We could simply issue a warning that says: "Either use `a?.b?.c`, or use `a == null ? null : a.b.c`, depending on whether you want to throw if `a != null && a.b == null`." + +There is exactly one sensible thing that `a?.b.c` could possibly mean: if `a` is null, skip over all chained member-access operations. You even recognize that as a sensible meaning. +> +> `a == null ? null : a.b.c` is not the main use case that the `a?.b` syntax is trying to solve, `a == null ? null : a.b` is. + +Who cares about the "main use case". Excessively narrowly targeting features toward particular use cases impairs languages' general usability. + +Also, given the expression a?.b?.c?.d, should the compiler's evaluate it as: + + typeOfD eval(typeOfA a) + { + var b = (a == null) ? null : a.b; + var c = (b == null) ? null : b.c; + var d = (d == null) ? null : c.d; + return d; + } + +or + + typeOfD eval(typeOfA a) + { + if (a==null) return null; + var b = a.b; + if (b == null) return null; + var c=b.c; + if (c == null) return null; + var d=c.d; + return d; + } + +I would suggest that if `a` is null, it makes no sense to evaluate any chained member accesses *whether or not they use the safe operator*. If all other accesses use the safe operator, continuing to evaluate them wouldn't hurt code semantics, but would either waste time, or compel the JITter expend otherwise-unnecessary efforts replacing the chained branches with a direct branch to the inevitable result. + +An important principle in language design is (or should be) that semantically-correct code should not have to be syntactically uglier than semantically-incorrect code. If no object's `c` member should ever return `null`, then the access of `d` from from an object's `c` member should fail if the `c` member does return `null`. If one can write code which silently ignores a null `c` as `a?.b?.c?.d` but the only way to trap on a null `c` value would be: + + var temp1 = a?b; + typeOfD result = (temp1 != null) ? null : temp1.c.d; // Should throw exception if temp1.c is null + +If programmers can write `a?b?c.d`, they're far more likely to write the correct code than if they can't. + +Incidentally, the utility of this concept could be made even greater if there were a way to specify the default value of the expression. Also, the same concepts applicable to member access should be equally applicable to indexers (probably with a `?[` syntax; basically say that if the `?` is followed by a member accessing operator or index, that operator will be applied to the left-hand operand. + + + +--- + +On Apr 25, 2014 at 2:49 AM @PauloMorgado wrote: + +Well put, supercat! + +Just one thing, there's already a way to specify the default value: the null coalescing operator. + +Let's say you want to treat null string references as having 0 length. You can do it in two ways: + +Now: + +``` +(myString ?? string.Empty).Length + +``` +Then: + +``` +myString?.Length ?? 0 + +``` + +--- + +On Apr 25, 2014 at 10:49 AM @Timwi wrote: + +@jvlppm, you seem to be assuming the left-associative semantics throughout your entire last post, while we’ve long established that the right-associative semantics is the desirable one. This alone addresses most points you raised. As for extension methods, we addressed that earlier: we established that there is a semantic difference between `a?.b.c()` and `(a?.b).c()`, and that the latter is useful only if `c()` is an extension method that tolerates `null`s. + +Finally, to address your statement that `It is a rare case in my opinion.` — this is not a matter of opinion, it is a measurable quantity. We should measure it by looking at our existing code. However, if you presume the left-associative semantics and I presume the right-associative one, then we will be measuring different things and not come to an agreement. As far as I’m concerned, I’m aware of several examples in my code where `a?.b.c` would be equivalent but shorter and it would make semantic sense (when applying the right-associative semantics). Thus, at least in *my* body of existing code, it is not at all a rare case. + +--- + +On Apr 25, 2014 at 1:16 PM @jvlppm wrote: + +Yes, I've noticed after my post that you were assuming a right-associative position, and I also agree that it is the best option. +As for "my opinion", it means that I really have no evidence for that, and it is not my job to collect that kind of data, it means that I'm gessing, and someone else should collect that data, if he wants to know for sure. + +My sugestion on using ```default(a.b.c)``` instead of ```a.?b.?c``` is relevant only when the operator .? is left-associative. +Because after the first .? you are forced to use .? through the rest of the expression, +and with my suggestion it would be clear that, the null is not carried on through the rest of the expression, otherwise it would result in a NullReferenceException in the ```.``` operator. Making the expression behave like right-associative in the end, but, it would leave no doubts of what happens when a null is found, +and there would be no left/right associative discussion. + +If you think that there is a mistake in my post, please ignore it, since this is not a formal proposition, and I am allowed (at least I think) to say my opinion and make mistakes. + + +--- + +On Apr 25, 2014 at 6:45 PM @supercat wrote: + +**jvlppm wrote:** +> ...it would leave no doubts of what happens when a null is found, + +Would you see any problem with allowing a means of explicitly specifying a default for a chained sequence of "?." and "." which should be used if anything in the chain is null, and requiring that it be specified if the last thing in the chain is a non-nullable value type? Perhaps say that `foo?bar : boz` would be equivalent to `foo != null ? foo.bar : boz`, but with the caveat that a `?.member` or `?[index]` should not appear as the second parameter to `? :` operator unless enclosed in parentheses? + +--- + +On Apr 30, 2014 at 2:42 PM @eldritchconundrum wrote: + +**supercat wrote:** +> **eldritchconundrum wrote:** +> > Having `foo?.BaseStream.Close()` throw when foo is null, to me, is the expected behaviour. +> > That's what a dot means, and people know how to use it. +> +> Then what's the `?.` for? + +With left-associativeness, it's meant to be used in `a?.b` or `a?.b?.c`, never `a?.b.c`. +That's why it would make sense to make it a warning. + + +> There is exactly one sensible thing that `a?.b.c` could possibly mean: if `a` is null, skip over all chained member-access operations. You even recognize that as a sensible meaning. + +I do see the benefits of that meaning. I also see a drawback. +Under right-associativity, we will not be able to refactor `a?.b.c()` as `var x = a?.b; x.c()`, and that is somewhat surprising. +Same remark for `a?.b.m()` and `ExtMeth.m(a?.b)` not being equivalent (if we use Olmo's answer to my ABCDEF examples). +Should every C# developers have to learn that '?.' does not have the same associativity than '.' before they can correctly do such trivial changes to code that use '?.' ? + +> Who cares about the "main use case". Excessively narrowly targeting features toward particular use cases impairs languages' general usability. + +So is breaking expectations about refactoring... It looks like we cannot have everything. + +I wonder what other languages do. I just checked Groovy's similar operator: their `a?.b.c` throws when a is null, so they seem to agree with you. +Well, maybe I should resign myself to having to be careful around `?.` when I refactor my code. + +--- + +On Apr 30, 2014 at 5:28 PM @lwischik wrote: + +**eldritchconundrum wrote:** +> Under right-associativity, we will not be able to refactor `a?.b.c()` as `var x = a?.b; x.c()`, and that is somewhat surprising. + +Sure you can refactor... +``` cs +return a?.b.c(); + +==> + +var x = a?.b; +return x?.c(); +``` + +When refactoring, you just have to introduce an additional null-check. Under right-associative, the null-check could be skipped before refactoring, but after refactoring then you're forced to use it. (Under left-associative, the extra null-check could never be skipped, so it looked the same before and after refactoring). + + + + +--- + +On Apr 30, 2014 at 6:45 PM @supercat wrote: + +I hadn't thought in particular about the refactoring issue you mention, though it actually points I think to a deeper issue which is that I dislike the assumption that `foo.bar.boz` should always be equivalent to `(foo.bar).boz;` Rather than regarding as evil anything which violates that assumption [such things already exist], I think it would be better to take the view that it should be possible for connections to exist between the later and earlier parts of an expression. The fact that such connections are rare doesn't mean they should be. In many cases, snapshots are easier to reason about than read-only views, , but read-only views are cheaper to construct and will be equivalent to snapshots if they're abandoned before any change to the underlying data. Given `var temp = foo.bar`, `foo` has no way of knowing whether it might be modified while `temp` is still alive, but given `foo.bar.boz`, if there were a way `foo` could know if the only thing done with the value of `bar` was to access one of its members, or if it could be told when it was used in any other fashion, `bar` could safely return a read-only view if none of its members persisted reference to themselves. + +--- + +On Apr 30, 2014 at 9:18 PM @lwischik wrote: + +Thank you for all the discussion, everyone. The C# Language Design Meeting met, reviewed everyone's comments, and agreed with right-associative. Particular thanks to Kathleen who provided slides that helped make the argument. + +https://roslyn.codeplex.com/discussions/543895 + +![Image](http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-06/0842.leftright.jpg) + + +Interestingly, it was pointed out after the fact that CoffeeScript also uses right-associative... for example http://coffeescript.org/#try:a%3F.b.c%3F.d + + +--- + +On Sep 19, 2014 at 7:26 PM @MarkPflug wrote: + +I fired up CTP3 to experiment with the null propagating operator feature and noticed something that could potentially be improved. +``` +var val = this?.Foo; +``` +I would expect that since "this" can never be null, this code would either produce a compiler warning or the compiler would simply eschew the null check. There is no warning, and looking at the generated code in ILSpy I see the null check. Admittedly, this is a pretty minor issue. + +--- + +On Sep 19, 2014 at 8:07 PM @supercat wrote: + +**MarkPflug wrote:** +> I would expect that since "this" can never be null, this code would either produce a compiler warning or the compiler would simply eschew the null check. There is no warning, and looking at the generated code in ILSpy I see the null check. Admittedly, this is a pretty minor issue. + +For methods of structure types, `this` cannot be null; for non-virtual methods of class types, however, it can. If class `Thing` has a method `void DoSomething(double x)`, that is semantically equivalent to `static void DoSomething(Thing it, double x)`, which may be assigned to a delegate of type `Action`. If the delegate is called `act`, then `act(foo,bar)` will be equivalent to `foo.DoSomething(bar)`, except that invoking the latter when `foo` is null will result in a call to the method with `this` being null. + +--- + +On Sep 19, 2014 at 8:23 PM @MarkPflug wrote: + +**supercat wrote:** +> **MarkPflug wrote:** +> > I would expect that since "this" can never be null, this code would either produce a compiler warning or the compiler would simply eschew the null check. There is no warning, and looking at the generated code in ILSpy I see the null check. Admittedly, this is a pretty minor issue. +> +> For methods of structure types, `this` cannot be null; for non-virtual methods of class types, however, it can. If class `Thing` has a method `void DoSomething(double x)`, that is semantically equivalent to `static void DoSomething(Thing it, double x)`, which may be assigned to a delegate of type `Action`. If the delegate is called `act`, then `act(foo,bar)` will be equivalent to `foo.DoSomething(bar)`, except that invoking the latter when `foo` is null will result in a call to the method with `this` being null. + +I don't quite understand how you get that delegate assignment to work? I understand everything that you are saying except how to get the instance member assigned as the delegate for Action? +I can do: +``` +Thing foo = null; +Action a = foo.DoSomething; +``` +However, the assignment on the second line throws an ArgumentException: "Delegate to an instance method cannot have null 'this'.". Which seems to be intended to explicitly avoid the scenario that you are describing. Can you show me a full code example that reproduces a null "this"? + + +--- + +On Sep 19, 2014 at 8:38 PM @MarkPflug wrote: + +Nevermind, I figured it out: +``` +Action a = (Action)Delegate.CreateDelegate(typeof(Action), null, typeof(Thing).GetMethod("DoSomething")); +``` +So "this" can be null. Learn something new every day. + +Though, I'm surprised that this call to CreateDelegate doesn't throw the same ArgumentException that my previous example threw. + +--- + +On Sep 19, 2014 at 10:17 PM @supercat wrote: + + class Bozo + { + public string name; + public void doSomething(double x) + { + if (this == null) + Console.WriteLine("I'm null; x is {0}", x); + else + Console.WriteLine("My name is {0} and x is {1}", name, x); + } + } + ... and to test it + static void testIt() + { + Bozo bozo1 = new Bozo(); bozo1.name = "Fred"; + Action act = bozo1.doSomething; + Action bozoActor = + (Action)Delegate.CreateDelegate(typeof(Action), act.Method); + bozoActor(bozo1,1.2); + bozoActor(null,1.4); + } + +Note that the CreateDelegate call doesn't see a null "this", since it's creating a delegate that will accept `this` as a parameter. If the method were virtual, calling the delegate would fail because the type of `this` would be needed to select a method, but for non-virtual methods it isn't needed. + +As to the reason things work that way, if languages had been required to use `call` rather than `callvirt` when invoking non-virtual instance members, that would have eliminated the need for the syntactically horrible `String.IsNullOrEmpty(someString)`, versus the syntactically cleaner `someString.IsNullOrEmpty`. + +--- + +On Sep 20, 2014 at 1:42 AM @Halo_Four wrote: + +**supercat wrote:** +> As to the reason things work that way, if languages had been required to use `call` rather than `callvirt` when invoking non-virtual instance members, that would have eliminated the need for the syntactically horrible `String.IsNullOrEmpty(someString)`, versus the syntactically cleaner `someString.IsNullOrEmpty`. + +From what I gather that was an explicit design decision made by the C# and VB.NET teams to always emit the `callvirt` IL opcode when calling an instance method due to the confusion of the behavior of being able to call methods on `null` references. + +[http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx](http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx) + +This is explicitly codified into the [C# Language Specification](http://www.microsoft.com/en-us/download/confirmation.aspx?id=7029) under section 7.5.5 "Function member invocation" where if the target "is null, a `System.NullReferenceException` is thrown and no further steps are executed.". + +Up until extension methods it was predictable that `foo.Bar()` would throw if `foo` was `null` regardless of how the method was implemented. The syntax you'd prefer could've been accomplished with extension properties which were cut from a previous release because, if I recall, the syntax was just awful looking. I imagine that it would be trivial to implement what you requested, potentially even just using an attribute to annotate the target method instead of adding any new syntax. But support for it from a consumer point of view would depend on changes made to every .NET-targeting compiler.. + +--- + +On Sep 20, 2014 at 4:24 AM @supercat wrote: + +**Halo_Four wrote:** +> From what I gather that was an explicit design decision made by the C# and VB.NET teams to always emit the `callvirt` IL opcode when calling an instance method due to the confusion of the behavior of being able to call methods on `null` references. + +That is my understanding as well, but the fact that invocation via delegates doesn't do a null check might predate that. Personally, I think there should have been an attribute to specify whether a method should be invokable on null, since there some methods where it makes sense and some where it doesn't. + +Actually, what might have been best would have been for C# to have included separate `.` and `->` operators, with the implication that `.` acts upon a *variable* and `->` acts upon a thing referenced by the variable. Field access would use `.` for structures and `->` for classes; for methods and properties, `.` would pass `this` by reference and `->` would pass it by value, *regardless of whether it was invoked on a class or struct. Obviously too late now, though. + +--- + +On Sep 20, 2014 at 12:37 PM @Halo_Four wrote: + +**supercat wrote:** +> **Halo_Four wrote:** +> > From what I gather that was an explicit design decision made by the C# and VB.NET teams to always emit the `callvirt` IL opcode when calling an instance method due to the confusion of the behavior of being able to call methods on `null` references. +> +> That is my understanding as well, but the fact that invocation via delegates doesn't do a null check might predate that. Personally, I think there should have been an attribute to specify whether a method should be invokable on null, since there some methods where it makes sense and some where it doesn't. +> + +Well we're talking about a decision made by the C# compiler team specifically regarding how it would behave on these method invocations. If you construct a delegate and invoke it directly you would be bypassing whatever safeguards/guarantees that the C# compiler attempts to enforce and exploiting the capabilities of the CLR, which of course does support this. + +> +> Actually, what might have been best would have been for C# to have included separate `.` and `->` operators, with the implication that `.` acts upon a *variable* and `->` acts upon a thing referenced by the variable. Field access would use `.` for structures and `->` for classes; for methods and properties, `.` would pass `this` by reference and `->` would pass it by value, *regardless of whether it was invoked on a class or struct. Obviously too late now, though. +> + +I recall that being another explicit design decision, not to have two kinds of accessor operators for standard use, following languages like Java and Visual Basic (pre-.NET) both of which have the same method invocation behavior. Of course C# does have a `->` operator but only to be used explicitly when working with pointers. + +I think it's one of those cases where just because something is possible doesn't mean that it should be done, even if there are a few cases where it might produce nicer-looking code. + +--- + +On Sep 22, 2014 at 11:40 PM @supercat wrote: + +**Halo_Four wrote:** +> I recall that being another explicit design decision, not to have two kinds of accessor operators for standard use, following languages like Java and Visual Basic (pre-.NET) both of which have the same method invocation behavior. Of course C# does have a `->` operator but only to be used explicitly when working with pointers. + +Structures and objects are totally different things in VB6. Since VB.NET did not exist prior to .NET, and structure member access can only be performed on things which are explicitly declared to be structures, a mechanical code parser could easily have converted struct field access to use a different operator from class member access when translating existing code. + +> I think it's one of those cases where just because something is possible doesn't mean that it should be done, even if there are a few cases where it might produce nicer-looking code. + +The reason there should have been a distinction IMHO is not because it would have made "nicer-looking" code, but rather because it would have made clear the distinction between operations which are performed upon a storage location, versus operations which are performed upon some outside object to which a storage location holds a reference. If C# and VB.NET had (for consistency with Java) used `.` as their equivalent to `->`, but had required the use of `.:` for struct field access or the invocation of methods which accept `this` as a writable byref, then the expected answers to the two questions below would be clear, even without having to know the types of the variables involved. + + someType foo1 = bar1; + otherType foo2 = bar2; + foo1.x = 23; // Does this affect bar1.x? + foo2.:y = 57; // Does this affect bar2.y? + +The write to `foo1.x` would be expected to modify a mutable class object to which `bar1` holds or encapsulates a reference, while the write to `foo2.:y` would be expected to modify the variable `foo2` (probably a struct, but potentially a reference to an immutable object), and not affect `bar2`. + +With the above design, it would likely not be possible to have an mutating interface that could be usefully be implemented both by a value type and by an inheritable class, but don't know that there's a real usage case for such things. While there are some cases where mutating interfaces are employed with structure types, most of them involve an effort to optimize `foreach` and would have (at least in retrospect) been better served by allowing the compiler to duck-type using a name like `GetForEachEnumerator` [which could be hidden in IntelliSense]. That would allow `GetEnumerator` to return a class object that implements `IEnumerator` even if `GetForEachEnumerator` returned a structure that didn't implement any interface but included `Current` and `MoveNext()`. + +--- + +On Sep 23, 2014 at 12:29 PM @Timwi wrote: + +``` C# +someType foo1 = bar1; +otherType foo2 = bar2; +foo1.x = 23; // Does this affect bar1.x? +foo2.:y = 57; // Does this affect bar2.y? +``` + +Try the following: + +* In Visual Studio, go to Tools → Options +* In the tree view on the left, under “Environment”, find “Fonts and Colors” +* In the list labeled “Display items”, find “User Types (Value types)” +* Set it to a different color. + +From now on, classes and structs are colored differently in your code. From now on, you can answer the above questions just by looking at the color of the type. Furthermore, because you can’t help but see the color every time you see the type, you will be far more acutely aware (with no extra effort on your part) which types are value types and which are not. + +(For obvious reasons, it is similarly beneficial to also change the color of enum types, interfaces and delegates. However, “User Types (Type parameters)” unfortunately doesn’t work, probably due to a bug.) + +I am utterly confused by the fact that Visual Studio has this incredibly useful feature, but doesn’t make use of it by default. Why are the colors set the same by default, ensuring that nobody ever finds out that this feature even exists? + +--- + +On Sep 23, 2014 at 2:32 PM @supercat wrote: + +Colors would help, but the feature I would have liked to have seen would have gone beyond that. Some reference types (like `string`) try to behave as values, and some value types behave like reference types [they encapsulate the identity of a mutable object, and changes to the object are reflected in the properties of the value type]. Allowing both value types and reference types to define members which accept `this` as a mutable byref and methods which accept `this` by value would have allowed the two kinds of things have more consistent behavior (class types would mostly use `.` members, and value types `.:` members, but either kind of type could offer either kind of member, and the behavior of each kind of member would be the same whether it was operating on a value type or a reference type. For example, an immutable class which used `.:` for all its member-access functions could e.g. define an `X` property as something like: + + int X { + ref get { return _x; } + ref put + { + var newValue = (this==null) ? new Thing() : (Thing)Clone(); + newValue._x = value; // Assume backing field is "private set" + return newValue; + } + +From a client code perspective, the semantics of `myThing.:X = 5;` would be equivalent to what they would have been if `X` was a structure. From a performance standpoint, code which frequently modifies individual members of the type could end up generating an excessive number of short-lived objects, but that is a general limitation with immutable classes anyhow. + +My main point is that there is a lot of code which expects to be working with values rather than entities; while .NET languages offer better support for values than does Java, they still often force programs to use reference semantics when value semantics would be more appropriate. Having separate syntax for things that behave with value- and reference semantics would facilitate the appropriate use of each [actually, a third type of semantics would help as well: "neutral" semantics--for things where value and reference semantics would be essentially equivalent]. + +--- + +On Sep 23, 2014 at 9:42 PM @Olmo wrote: + +**Timwi wrote:** + +> I am utterly confused by the fact that Visual Studio has this incredibly useful feature, but doesn’t make use of it by default. Why are the colors set the same by default, ensuring that nobody ever finds out that this feature even exists? + + +. ++1 for "Colors for structs, enums, delegates and interfaces by default!" we have been using this for some time now, here is the vssettings: + +https://raw.githubusercontent.com/signumsoftware/framework/master/Snippets/Signum.Fonts.vssettings + +--- + +On Oct 19, 2014 at 8:01 AM @lwischik wrote: + +**Timwi wrote:** +> I am utterly confused by the fact that Visual Studio has this incredibly useful feature, but doesn’t make use of it by default. Why are the colors set the same by default, ensuring that nobody ever finds out that this feature even exists? + +:) During the development of VS2012, the C++ language service was completely rewritten. They made the default use different colours for absolutely everything. I filed issues on the team complaining that it looked like fruit salad. I guess other people agreed with me, because they scaled the colors down massively before VS2012 was released. + +VS14 will apparently have more customizable colors for VB. (maybe also for C#? I'm not sure). They'll be in the next preview release after CTP4. + + +-- Lucian Wischik, VB/C# team + +--- + +On Oct 26, 2014 at 10:00 PM @Olmo wrote: + +Trying this in VS 14 CTP + +```C# +Expression> func = s => s?.ToString(); +``` + +I get a compile-time error: + +``` +Error 1 An expression tree lambda may not contain a null propagating operator. c:\users\olmo.signums\Documents\Visual Studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs 14 58 ConsoleApplication1 +``` + +Is this work-in-progress or it won't be allowed in expression trees? I that's the case if find it really disappointing. + +Expression trees are commonly used for LINQ providers that are translated to SQL. In there navigation properties are typically used to simplify `LEFT OUTER JOINS`. + +SQL, by default, will always propagate the nullability implicitly. So a query like this: + +```C# +List ids = orders.Select(o=>o.Customer.Id).ToList(); +``` + +Even if is statically typed as `List` for C#, if there are orders without customer SQL will try to return null. Then the LINQ provider has two options. + +* Throw an exception because null doesn't fit into `int?`, forcing the user to convert the query to: +```C# +List ids = orders.Select(o=>(int?)o.Customer.Id).ToList(); +List ids = orders.Where(o=>o.Customer != null).Select(o=>o.Customer.Id).ToList(); +``` +* Just convert the null to `default(int)` (aka 0), creating misleading results. + +The best solution will be to be able to express in C# the SQL behaviour more accurately. +```C# +List ids = orders.Select(o=>o.Customer?.Id).ToList(); +``` + +Letting the user have their own default value: + +```C# +List ids = orders.Select(o=>o.Customer?.Id ?? -1).ToList(); +``` + +In order to do the translation allowing in general member access without duplicating all the possible followings of `?.` (MethodCallExpression, `MemberExpression`, etc... a new `NullPropagationExpression` will be necessary: + +```C# + public class NullPropagationExpression : Expression + { + public Expression Object { get; private set; } + public ParameterExpression MemberAccessParameter { get; private set; } + public Expression MemberAccessBody { get; private set; } + + public NullPropagationExpression(Expression @object, ParameterExpression parameter, Expression body) + { + this.Object = @object; + this.MemberAccessParameter = parameter; + this.MemberAccessBody = body; + } + } +``` + +So if currently this: + +```C# +Expression> func = s => s.ToString(); +``` + +Is translated to + +```C# +ParameterExpression s; +Expression> func = Expression.Lambda>( + Expression.Call(s = Expression.Parameter(typeof(string), "s"), (MethodInfo)methodof(object.ToString)), + s); +``` + +The using null-propagation: + +```C# +Expression> func2 = s => s?.ToString(); +``` + +Will be translated to + +```C# +ParameterExpression s; +ParameterExpression p; +Expression> func = Expression.Lambda>( + Expression.NullPropagation( + @object: s = Expression.Parameter(typeof(string), "s"), + parameter: p = Expression.Parameter(typeof(string)), + body: Expression.Call(p, (MethodInfo)methodof(object.ToString))), + s); +``` + + + +--- + +On Oct 27, 2014 at 10:47 PM @lwischik wrote: + +Olmo, thanks for writing that up. + +So far we haven't been adding new features to expression-trees. That was sort of okay for "await" and "dynamic" but I agree that ?. would be more valuable. So would statement-bodied lambdas. + +I guess we've always had the concern about whether the new feature should be expressed in terms of existing constructs (so existing expression-tree-parsers would accommodate it fine) or new constructs (in which case all existing parsers would need to be updated, otherwise they'll crash at runtime). Also, the expression-tree work always seemed lower value than the other things we could be doing. + +How to move forward? One option is a uservoice item to see if it's a common need that our team should address ourselves. Another option is for the community to work towards an grand overarching design for expression-trees in general, and identify the one small ?. corner of them, and make sure that it's coherent with the over-arching design, and submit a pull-request for it. + +-- Lucian, VB/C# team + + + +--- + +On Oct 28, 2014 at 9:14 AM @Olmo wrote: + +**lwischik wrote:** +> Olmo, thanks for writing that up. +> +> So far we haven't been adding new features to expression-trees. That was sort of okay for "await" and "dynamic" but I agree that ?. would be more valuable. So would statement-bodied lambdas. +> +> I guess we've always had the concern about whether the new feature should be expressed in terms of existing constructs (so existing expression-tree-parsers would accommodate it fine) or new constructs (in which case all existing parsers would need to be updated, otherwise they'll crash at runtime). Also, the expression-tree work always seemed lower value than the other things we could be doing. + +The existing parsers will need to update in order to support the new feature. This is no different than just creating a new method that should be considered, like translating `string.IsNullOrEmptyOrWhiteSpace` to SQL. Still, I prefer it to be translated to conditional expression that not translated at all! + +> How to move forward? One option is a uservoice item to see if it's a common need that our team should address ourselves. Another option is for the community to work towards an grand overarching design for expression-trees in general, and identify the one small ?. corner of them, and make sure that it's coherent with the over-arching design, and submit a pull-request for it. + +As you said, [Add "?." operator to C#](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c) is already the second more voted request, and I don't think that you need to rise a question in uservoice to make a feature _complete_. + +Specially, asking for expression trees support won't we fair. Expression trees are designed to be hidden for most of the developers (just the ones writing LINQ providers really care about them) but many developers just use them without knowing. I'm sure many people in this 5,439 votes is using Entity Framework and will be surprised that it doesn't work. + +As I said before, this feature is important for LINQ providers because it reflects the exact behavior of the database when navigating properties using `LEFT JOIN`, I understand that adding support for `await`, `dynamic`, or general statements `if`/ `for` / `while`... will not pay for itself, but null-propagation is how databases work. It's like not having support for `Nullable` in the database. + +If I try to make the pull-request myself: + +* Is there a chance that if will be shipped in VS 14? or this train is already gone? +* The dependency from the Roslyn compiler to `System.Linq.Expressions.Expression` is just duck-typed as usual or is there a strong-name dependency? I will need to call the `NullPropagationExpression` constructor directly since I can not add a method to `Expression` class, but if I create a class in the `System.Linq.Expressions` namespace but my own assembly maybe it will work? I will need that somebody also adds the `VisitNullPropagation` in `ExpressionVisitor`, and `ExpressionType`. + +Thanks in advance. + +--- + +On Oct 29, 2014 at 1:30 AM @darkman666 wrote: + +As I understand, Linq.Expressions is supported by DLR. According to their documentation (https://dlr.codeplex.com/wikipage?title=Docs%20and%20specs&referringTitle=Documentation, https://github.com/IronLanguages/main), if language need to add new Expression node, it can be done by implementing custom type derived from Expression that will implement proper method `System.Linq.Expressions.Expression Reduce()`, that will provide the same expression with only well-know expressions types. +For `NullPropagationExpression` it can provide implementation with ConditionalExpression. At least Compile method initially call Reduce, same can do expression provider if it wants - or work with simple `NullPropagationExpression`. + +--- + +On Oct 29, 2014 at 9:21 AM @Olmo wrote: + +I've been thinking about it, and the C# 6 compiler is going to be used with .Net 2.0, 3.0, 3.5, 4.0... none of them will have `NullPropagationExpression` defined. So maybe translating it to `ConditionalExpression` is a better approach. + +--- + +On Oct 29, 2014 at 11:01 AM @BachratyGergely wrote: + +I'm not convinced this is a major issue. From 3.5 to 4.0 a bunch of new expression types were introduced and the `ExpressionVisitor` extended. + +--- + +On Oct 29, 2014 at 12:12 PM @Olmo wrote: + +Sure, but not in the C# compiler generator. The thing is that there is an scenario of someone writing this in any other version of .Net + +```C# +ctx.Invoices.Select(i=>i.Customer?.Name).ToList() +``` + +And this won't work for anybody using something older than .Net 4.5.3 if we go for `NullPropagationExpression`. While using `ConditionalExpression` it will work. + +--- + +On Oct 29, 2014 at 4:59 PM @BachratyGergely wrote: + +**Olmo wrote:** +> Sure, but not in the C# compiler generator. The thing is that there is an scenario of someone writing this in any other version of .Net +> +> ```C# +> ctx.Invoices.Select(i=>i.Customer?.Name).ToList() +> ``` +> +> And this won't work for anybody using something older than .Net 4.5.3 if we go for `NullPropagationExpression`. While using `ConditionalExpression` it will work. + +Of course it will work. You only have to create a `NullPropagationExpression` in your project with the exact same surface (namespace and members) as the compiler expects it. This is the same technique LinqBridge lets you do Linq stuff with C# 5.0 targeting .NET 2.0 which never even heard of expressions. + +The problem with `ConditionalExpression` is that if the left side of `a?.b` has side effects then it is not a trivial `a != null ? a.b : null` but rather it would become `{ var tmp = a; return tmp != null ? tmp.b : null;}`. And on the consumer side that would be really hard to determine that this was a null propagation expression originally. + +--- + +On Oct 31, 2014 at 12:33 AM @Olmo wrote: + +**BachratyGergely wrote:** +> **Olmo wrote:** +> > Sure, but not in the C# compiler generator. The thing is that there is an scenario of someone writing this in any other version of .Net +> > +> > ```C# +> > ctx.Invoices.Select(i=>i.Customer?.Name).ToList() +> > ``` +> > +> > And this won't work for anybody using something older than .Net 4.5.3 if we go for `NullPropagationExpression`. While using `ConditionalExpression` it will work. +> +> Of course it will work. You only have to create a `NullPropagationExpression` in your project with the exact same surface (namespace and members) as the compiler expects it. This is the same technique LinqBridge lets you do Linq stuff with C# 5.0 targeting .NET 2.0 which never even heard of expressions. + +Is not that simple. Even if you're happy with this new expression not being completely integrated (in NodeType, ExpressionVisitor, Expression factory method), you solution will work with duck-typing for the compiler, but not for the libraries that will require it. For example, a project written in .Net 4.5 using EntityFramework and MVC will be interested in using ?. both for LINQ queries end for `EditFor` fields. They will need to agree in one mocked implementation, maybe available via NuGet?. Looks too much hassle for few value. + +> The problem with `ConditionalExpression` is that if the left side of `a?.b` has side effects then it is not a trivial `a != null ? a.b : null` but rather it would become `{ var tmp = a; return tmp != null ? tmp.b : null;}`. And on the consumer side that would be really hard to determine that this was a null propagation expression originally. + +It will be relatively simple to build a ExpressionVisitor that converts compiled-generated `ConditionalExpression` to the equivalent `NullPropagationExpression`. For most of the use cases that is _good enough_. + +--- + +On Oct 31, 2014 at 1:25 AM @Olmo wrote: + +**PULL REQUEST** + +This is my first attempt to make the C# compiler translate `?.` to expression trees. It's fully working but I'm sure the code could be improved. + +https://roslyn.codeplex.com/SourceControl/network/forks/Olmo/NullPropagationExpresions/contribution/7642 + + + + + +--- + +On Oct 31, 2014 at 5:52 PM @BachratyGergely wrote: + +**Olmo wrote:** +> Is not that simple. Even if you're happy with this new expression not being completely integrated (in NodeType, ExpressionVisitor, Expression factory method), you solution will work with duck-typing for the compiler, but not for the libraries that will require it. For example, a project written in .Net 4.5 using EntityFramework and MVC will be interested in using ?. both for LINQ queries end for `EditFor` fields. They will need to agree in one mocked implementation, maybe available via NuGet?. Looks too much hassle for few value. + +There's already a lot of stuff in Expressions not supported in Entity Framework. E.g. the ternary operator and the if-else statement are both represented by a `ConditionalExpression` but only the former is handled correctly. Stuff that is aware of this feature certainly has builds that reference the framework implementation, there is no need to agree on anything. +EF never evaluates these expressions. There's no reason to use `?.` instead of `.`. What would be the your use case for MVC? + +> It will be relatively simple to build a ExpressionVisitor that converts compiled-generated `ConditionalExpression` to the equivalent `NullPropagationExpression`. For most of the use cases that is _good enough_. + +The expression visitor would have to drill down into the expression tree every time it sees a `ConditionalExpression`. This is not really performant and breaks the basic visitor pattern by evaluating much more than just the current expression node and direct properties. Most importantly whenever the generated pattern changes (for any reason) the visitor also has to be updated to recognize both the old pattern and the new pattern since the libraries built with the previous compiler do not change. +It's much more reasonable to add a suitable `Reduce()` implementation. You can actually implement this in a way it automatically expands to `ConditionalExpression` when processed by the `ExpressionVisitor` based on e.g. app supported runtime version. + +--- + +On Nov 1, 2014 at 3:24 PM @Olmo wrote: + +**BachratyGergely wrote:** +> **Olmo wrote:** +> > Is not that simple. Even if you're happy with this new expression not being completely integrated (in NodeType, ExpressionVisitor, Expression factory method), you solution will work with duck-typing for the compiler, but not for the libraries that will require it. For example, a project written in .Net 4.5 using EntityFramework and MVC will be interested in using ?. both for LINQ queries end for `EditFor` fields. They will need to agree in one mocked implementation, maybe available via NuGet?. Looks too much hassle for few value. +> +> There's already a lot of stuff in Expressions not supported in Entity Framework. E.g. the ternary operator and the if-else statement are both represented by a `ConditionalExpression` but only the former is handled correctly. Stuff that is aware of this feature certainly has builds that reference the framework implementation, there is no need to agree on anything. + +Currently there's two types of expressions in System.Expressions: + +* **Expression that represent expressions:** Created in .Net 3.5, they are generated by the C# compiler and expected to be supported by a LINQ provider. This group include Add, And, Call, Lambda and in general everything in `ExpressionType` enum from value 0 to 45. + +* **Expression that represent statements:** Created in .Net 4, they are **not** generated by the C# compiler and **not** expected to be supported by LINQ providers, but are useful to create dynamic methods and I think they are also used by the DLR. This group include Assign, Block, Goto, Throw, and in general everything in `ExpressionType` enum from value 46 to 84. + +I think `?.` should be in the first group, be generated by the compiler and used seamlessly in LINQ providers, just as `??` or `? :`. + +> EF never evaluates these expressions. There's no reason to use `?.` instead of `.`. What would be the your use case for MVC? + +Not sure if i understood you. I think it makes sense to make this query using entity framework: + +```C# +List ids = ctx.Orders.Select(o=>o.Customer?.Id).ToList(); +``` + +And also makes sense to create this textbox in MVC: + +```C# +@Html.TextBoxFor(customer => customer.Address?.City) +``` + +> > It will be relatively simple to build a ExpressionVisitor that converts compiled-generated `ConditionalExpression` to the equivalent `NullPropagationExpression`. For most of the use cases that is _good enough_. +> +> The expression visitor would have to drill down into the expression tree every time it sees a `ConditionalExpression`. This is not really performant and breaks the basic visitor pattern by evaluating much more than just the current expression node and direct properties. Most importantly whenever the generated pattern changes (for any reason) the visitor also has to be updated to recognize both the old pattern and the new pattern since the libraries built with the previous compiler do not change. +> It's much more reasonable to add a suitable `Reduce()` implementation. You can actually implement this in a way it automatically expands to `ConditionalExpression` when processed by the `ExpressionVisitor` based on e.g. app supported runtime version. + + + +If there will be a way to patch all the .Net 3.5/4.0/4.5 out there I'll agree with but making a (duck-type) dependency from the C# compiler to `NullPropagationExpression` has some nasty consequences that I didn't though in my first proposal: + +* Projects using .Net 4.5.3 could use the feature without problems, because `NullPropagationExpression` will be defined. +* Projects using any older framework will have to create their own `System.Linq.Expressions.NullPropagationExpression` class, here the problems start: + * A library (like EntityFramework) will have to create his own class, but what if another library (like ASP.Net MVC) also wants to use the feature? They will create another `System.Linq.Expressions.NullPropagationExpression` in a different assembly. How will then the C# compiler know which class to instantiate?. + * There could be a official Nuget package for that. Bit is this worth just for one class? And I think there could still be issues with libraries that could be used in 4.5.0/1/2/3 at the same time, because the 4.5.3 will already include the class. + +Still, even if .Net 4.5.3 will be the only supported Framework, by using `ConditionExpression` all the LINQ providers that already support `?:` will support `?.` from day one, maybe generating some additional `CASE` in SQL, or evaluating the property twice, but it will work. Then the next version could do some optimizations by recognizing the pattern. + +I've build a simple project that recognizes the `ConditionExpression` and translates them to an **internal** `NullPropagationExpression`. + +https://github.com/olmobrutall/NullPropagation/tree/master/NullPropagation + +The code is not trivial and requires `ExpressionComparer` but is just one step more in the many that LINQ providers have to do already in order to translate to the target language (SQL for example). + + +--- + +On Nov 1, 2014 at 10:20 PM @BachratyGergely wrote: + +**Olmo wrote:** +> Currently there's two types of expressions in System.Expressions: +> +> * **Expression that represent expressions:** Created in .Net 3.5, they are generated by the C# compiler and expected to be supported by a LINQ provider. This group include Add, And, Call, Lambda and in general everything in `ExpressionType` enum from value 0 to 45. +> +> * **Expression that represent statements:** Created in .Net 4, they are **not** generated by the C# compiler and **not** expected to be supported by LINQ providers, but are useful to create dynamic methods and I think they are also used by the DLR. This group include Assign, Block, Goto, Throw, and in general everything in `ExpressionType` enum from value 46 to 84. +> +> I think `?.` should be in the first group, be generated by the compiler and used seamlessly in LINQ providers, just as `??` or `? :`. + +Right now the compiler generates statements for `?.`: extracting the left part to a temporary variable. To do the exact same with non-statement expressions only (without adding/extending expression types) you would get something like `a.b?.c.d` => `new[] { a.b }.Select(t => t.c.d).DefaultIfEmpty()`. AFAIK EF does not allow new-array expressions so this would break anyway. Do you have a better pattern in mind? +`??` and `?:` are not expanded to anything and have their dedicated expression classes. `??` is a BinaryExpression with `ExpressionType.Coalesce` and ?: is a ConditionalExpression with `ExpressionType.Conditional` and a non-`void` return type (if the return type is `void` then it's an if-statement). + +> Not sure if i understood you. I think it makes sense to make this query using entity framework: +> +> ```C# +> List ids = ctx.Orders.Select(o=>o.Customer?.Id).ToList(); +> ``` + +EF already treats `.` as `?.` semantically. This is a consequence of pretty much all SQL implementations doing null-propagation and unfortunate since it deviates from the IQeryable contract, but implementing it "right" would be both hard and slow. +The only thing missing is inferring a nullable type for materialization. So the above is the same as +``` +ctx.Orders.Select(o => (int?)o.Customer.Id).ToList(); +``` +If `Id` were already a nullable type (e.g. `string`) then there would be absolutely no difference between `o.Customer.Id` and `o.Customer?.Id`. That is for EF6. There might be changes with null handling in EF7. + +> And also makes sense to create this textbox in MVC: +> +> ```C# +> @Html.TextBoxFor(customer => customer.Address?.City) +> ``` + +As far as I know MVC helpers do not support conditional expressions in html helpers at all. There would be no point in translating `?.` to `ConditionalExpression` since it would throw the following `InvalidOperationExpression`: "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions." +Since MVC is never the producer of such expressions it would only need to consume other implementations, and it can do that without binding to a concrete type. +How would model binding work? Let's assume you put this on a form with Address being null and post it as is, without changing any fields. What would be the result? Should the model binder create an Address object? Then the result would not be the same that was passed to the view. Should it keep the Address property as null? How should it detect from the posted form whether the Address entity should be created or not? + +> If there will be a way to patch all the .Net 3.5/4.0/4.5 out there I'll agree with but making a (duck-type) dependency from the C# compiler to `NullPropagationExpression` has some nasty consequences that I didn't though in my first proposal: +> +> * Projects using .Net 4.5.3 could use the feature without problems, because `NullPropagationExpression` will be defined. +> * Projects using any older framework will have to create their own `System.Linq.Expressions.NullPropagationExpression` class, here the problems start: +> * A library (like EntityFramework) will have to create his own class, but what if another library (like ASP.Net MVC) also wants to use the feature? They will create another `System.Linq.Expressions.NullPropagationExpression` in a different assembly. How will then the C# compiler know which class to instantiate?. +> * There could be a official Nuget package for that. Bit is this worth just for one class? And I think there could still be issues with libraries that could be used in 4.5.0/1/2/3 at the same time, because the 4.5.3 will already include the class. + +The entire LINQ language feature, async and dynamic support are all implemented by a "duck-type" dependency from the C# compiler to the framework libraries and they depend on much more than just a single class. The caller member attributes are implemented with the same technique. Using these features already have the exact same problems and the exact same solutions as would upgrading the Expression API. Async does have a dedicated nuget package though much more restricted than the 4.5 implementation due to some supporting things missing from the older frameworks. + +> Still, even if .Net 4.5.3 will be the only supported Framework, by using `ConditionExpression` all the LINQ providers that already support `?:` will support `?.` from day one, maybe generating some additional `CASE` in SQL, or evaluating the property twice, but it will work. Then the next version could do some optimizations by recognizing the pattern. +> I've build a simple project that recognizes the `ConditionExpression` and translates them to an **internal** `NullPropagationExpression`. +> +> https://github.com/olmobrutall/NullPropagation/tree/master/NullPropagation +> +> The code is not trivial and requires `ExpressionComparer` but is just one step more in the many that LINQ providers have to do already in order to translate to the target language (SQL for example). + +Based on your code you seem to assume `a.b?.c.d` should be translated to `a.b != null ? a.b.c.d : null`. This is not the pattern and semantics used by the current compiler. What you propose makes `(Func)a => a.b?.c.d` and `((Expression)a => a.b?.c.d).Compile()` behave differently which would be unexpected by most coders. Also for chained null-propagations the number of evaluations per property grows exponentially, e.g. in `a?.b?.c?.d` `c` is evaluated 2 times, `b` 4 times, `a` 8 times. +Don't forget to implement method invoke (`?.Foo(...)`) and array element access (`?[...]`). + + +--- + +On Nov 2, 2014 at 10:11 AM @Olmo wrote: + +**BachratyGergely wrote:** +> **Olmo wrote:** +> ... +> Right now the compiler generates statements for `?.`: extracting the left part to a temporary variable. To do the exact same with non-statement expressions only (without adding/extending expression types) you would get something like `a.b?.c.d` => `new[] { a.b }.Select(t => t.c.d).DefaultIfEmpty()`. AFAIK EF does not allow new-array expressions so this would break anyway. Do you have a better pattern in mind? +> `??` and `?:` are not expanded to anything and have their dedicated expression classes. `??` is a BinaryExpression with `ExpressionType.Coalesce` and ?: is a ConditionalExpression with `ExpressionType.Conditional` and a non-`void` return type (if the return type is `void` then it's an if-statement). + +If evaluating the left part once is mandatory, I will gravitate back to create a new `NullPropagationExpression`. That's the best solution moving forward but will be harder to use in projects using .Net Framework less or equal than 4.5.3. + +> > Not sure if i understood you. I think it makes sense to make this query using entity framework: +> > +> > ```C# +> > List ids = ctx.Orders.Select(o=>o.Customer?.Id).ToList(); +> > ``` +> +> EF already treats `.` as `?.` semantically. This is a consequence of pretty much all SQL implementations doing null-propagation and unfortunate since it deviates from the IQeryable contract, but implementing it "right" would be both hard and slow. +> The only thing missing is inferring a nullable type for materialization. So the above is the same as +> ``` +> ctx.Orders.Select(o => (int?)o.Customer.Id).ToList(); +> ``` +> If `Id` were already a nullable type (e.g. `string`) then there would be absolutely no difference between `o.Customer.Id` and `o.Customer?.Id`. That is for EF6. There might be changes with null handling in EF7. + +I completely agree, and I tried to explain it in: https://roslyn.codeplex.com/discussions/571077. + +I'm still not happy with the current status-quo for value types: It's a counter-intuitive problem (default null propagation) with a counter-intuitive solution (casting an int to int? produces a null value). With `?.` in expressions at least the solution will make sense to a developer. + +> > And also makes sense to create this textbox in MVC: +> > +> > ```C# +> > @Html.TextBoxFor(customer => customer.Address?.City) +> > ``` +> +> As far as I know MVC helpers do not support conditional expressions in html helpers at all. There would be no point in translating `?.` to `ConditionalExpression` since it would throw the following `InvalidOperationExpression`: "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions." + +I think when designing a language we also have to consider the scenarios that it will enable, not what is possible with the current libraries. Adding support for it shouldn't be hard for the MVC tem. + +> Since MVC is never the producer of such expressions it would only need to consume other implementations, and it can do that without binding to a concrete type. + +Like using reflection to access `NullPropagationExpression` you mean? Sound bad. And someone should define the type in .Net 4.0 anyway. + +> How would model binding work? Let's assume you put this on a form with Address being null and post it as is, without changing any fields. What would be the result? Should the model binder create an Address object? Then the result would not be the same that was passed to the view. Should it keep the Address property as null? How should it detect from the posted form whether the Address entity should be created or not? + +ModelBinder is a different topic, maybe they are just read-only fields for example. But yes, in the case of an address, it could make sense to create a ModelBinder that returns a null address if all the address fields are null, and otherwise returns an proper address and tries to validate it. + +> +> > If there will be a way to patch all the .Net 3.5/4.0/4.5 out there I'll agree with but making a (duck-type) dependency from the C# compiler to `NullPropagationExpression` has some nasty consequences that I didn't though in my first proposal: +> > +> > * Projects using .Net 4.5.3 could use the feature without problems, because `NullPropagationExpression` will be defined. +> > * Projects using any older framework will have to create their own `System.Linq.Expressions.NullPropagationExpression` class, here the problems start: +> > * A library (like EntityFramework) will have to create his own class, but what if another library (like ASP.Net MVC) also wants to use the feature? They will create another `System.Linq.Expressions.NullPropagationExpression` in a different assembly. How will then the C# compiler know which class to instantiate?. +> > * There could be a official Nuget package for that. Bit is this worth just for one class? And I think there could still be issues with libraries that could be used in 4.5.0/1/2/3 at the same time, because the 4.5.3 will already include the class. +> +> The entire LINQ language feature, async and dynamic support are all implemented by a "duck-type" dependency from the C# compiler to the framework libraries and they depend on much more than just a single class. The caller member attributes are implemented with the same technique. Using these features already have the exact same problems and the exact same solutions as would upgrading the Expression API. Async does have a dedicated nuget package though much more restricted than the 4.5 implementation due to some supporting things missing from the older frameworks. + +Well, it's impossible to add a new factory method to Expression class, a new value to ExpressionType enum, and libraries could have conflicts if they define their own `NullPropagationExpression`. It also looks like a tiny feature for taking a dependency an downloading a Nuget. But I think there's a third solution that we can explore. I will publish it in a moment in https://roslyn.codeplex.com/discussions/571077. + +> > Still, even if .Net 4.5.3 will be the only supported Framework, by using `ConditionExpression` all the LINQ providers that already support `?:` will support `?.` from day one, maybe generating some additional `CASE` in SQL, or evaluating the property twice, but it will work. Then the next version could do some optimizations by recognizing the pattern. +> > I've build a simple project that recognizes the `ConditionExpression` and translates them to an **internal** `NullPropagationExpression`. +> > +> > https://github.com/olmobrutall/NullPropagation/tree/master/NullPropagation +> > +> > The code is not trivial and requires `ExpressionComparer` but is just one step more in the many that LINQ providers have to do already in order to translate to the target language (SQL for example). +> +> Based on your code you seem to assume `a.b?.c.d` should be translated to `a.b != null ? a.b.c.d : null`. This is not the pattern and semantics used by the current compiler. What you propose makes `(Func)a => a.b?.c.d` and `((Expression)a => a.b?.c.d).Compile()` behave differently which would be unexpected by most coders. Also for chained null-propagations the number of evaluations per property grows exponentially, e.g. in `a?.b?.c?.d` `c` is evaluated 2 times, `b` 4 times, `a` 8 times. + +I don't think this is an issue for LINQ providers because the code is usually side-effect free, the current design of the operator promotes few `?.` (is short circuiting) and the code will be translated to SQL (or similar) anyway, but is definitely an issue when using `?.` in order to create templates for code generation. + +> Don't forget to implement method invoke (`?.Foo(...)`) and array element access (`?[...]`). + +The implementation was not tied to MemberAccessExpression, but there where no test cases for delegate invoke, array access or indexers. I've added those and fix the ToString. + + +--- + +On Nov 3, 2014 at 1:44 PM @BachratyGergely wrote: + +> I think when designing a language we also have to consider the scenarios that it will enable, not what is possible with the current libraries. Adding support for it shouldn't be hard for the MVC tem. + +Write the spec on how MVC should behave in all possible places where it may encounter a null-propagation expression (in either form). Just the spec, no implementation. Then we'll see what "shouldn't be hard". +By the way I'm not even sure there would be a legacy version. MVC jumped to 4.5 without providing support for any older framework. + +> Like using reflection to access `NullPropagationExpression` you mean? Sound bad. And someone should define the type in .Net 4.0 anyway. + +No, like using the `NodeType` property and the built-in utility classes. E.g. MVC falls back to `LambdaExpression.Compile` to get the value. Since `LambdaExpression.Compile` already handles `?.` MVC uses it without actually knowing about it. The same can be done with `ExpressionVisitor`. There is an awful lot you can (and should) do before falling back to reflection. + +> ModelBinder is a different topic, maybe they are just read-only fields for example. But yes, in the case of an address, it could make sense to create a ModelBinder that returns a null address if all the address fields are null, and otherwise returns an proper address and tries to validate it. + +So you say extending MVC with `?.` would be useful only in an even mode limited scenario? How often would you use `HtmlHelper.TextBoxFor` for readonly fields? Does it really justify the costs? You can implement your own helper extensions and plug in your own model binder. Why should the MVC team implement a feature that has such limited scope and you can do it yourself if you need it? + +How would the ModelBinder handle values? Should it evaluate them twice: once for determining the class should be instantiated once for setting the fields? Should it call the type converters twice? What if the request stream is non-buffered? What would be the exact condition to determine that no class should be instantiated? Value == null? Value == default(T)? What about empty string and null? + +> Well, it's impossible to add a new factory method to Expression class, a new value to ExpressionType enum, and libraries could have conflicts if they define their own `NullPropagationExpression`. It also looks like a tiny feature for taking a dependency an downloading a Nuget. But I think there's a third solution that we can explore. I will publish it in a moment in https://roslyn.codeplex.com/discussions/571077. + +The compiler does not bind to "the" Expression class. The compiler binds to "an" expression class that has static factory methods with the required signatures. You can write a perfect wrapper to System.Linq.Expressions.Expression with your custom extensions that forwards existing calls to the real Expression factories and the compiler will pick yours. +Enums can have any value allowed by the underlying type. + +--- + +On Nov 3, 2014 at 2:42 PM @Olmo wrote: + +Hey man I don't know from where all this negativity comes... please take a loot at the third proposal. https://roslyn.codeplex.com/discussions/571077 I think is a nice middle ground. + +**BachratyGergely wrote:** +> > I think when designing a language we also have to consider the scenarios that it will enable, not what is possible with the current libraries. Adding support for it shouldn't be hard for the MVC tem. +> +> Write the spec on how MVC should behave in all possible places where it may encounter a null-propagation expression (in either form). Just the spec, no implementation. Then we'll see what "shouldn't be hard". + +I said it's easy because I've already implemented that. We have our own set of helpers in Signum.Web and they support our `Try` method (already explained above). Is in [this file]( https://github.com/signumsoftware/framework/blob/master/Signum.Web/TypeContext/TypeContextExpression.cs) in line 168. Is not that we use this feature every day, but sometimes is handy. Not sure if there's some MVC specific issue that I'm not aware. + +> By the way I'm not even sure there would be a legacy version. MVC jumped to 4.5 without providing support for any older framework. + +Sure, I also assume that the new versions of the libraries that will support the feature will be updates of the latest one (MVC 6) not patches on older versions (MVC 4.3) but still worth to mention. The new solution is more future-oriented. + +> > Like using reflection to access `NullPropagationExpression` you mean? Sound bad. And someone should define the type in .Net 4.0 anyway. +> +> No, like using the `NodeType` property and the built-in utility classes. E.g. MVC falls back to `LambdaExpression.Compile` to get the value. Since `LambdaExpression.Compile` already handles `?.` MVC uses it without actually knowing about it. The same can be done with `ExpressionVisitor`. There is an awful lot you can (and should) do before falling back to reflection. + +Not sure if I've understood you. You stated saying "and it can do that without binding to a concrete type.", whats the difference if you bind to the new NodeType value. Anyway, I think the third solution should glad you. + +> > ModelBinder is a different topic, maybe they are just read-only fields for example. But yes, in the case of an address, it could make sense to create a ModelBinder that returns a null address if all the address fields are null, and otherwise returns an proper address and tries to validate it. +> +> So you say extending MVC with `?.` would be useful only in an even mode limited scenario? How often would you use `HtmlHelper.TextBoxFor` for readonly fields? Does it really justify the costs? You can implement your own helper extensions and plug in your own model binder. Why should the MVC team implement a feature that has such limited scope and you can do it yourself if you need it? + +Sure. My point is not about MVC. The `?.` main use is for LINQ providers. I was just bringing MVC to focus on the possible conflicts that could arise if two libraries implement `NullPropagationExpression`. Anyway this issue is solved in the new solution. + +> How would the ModelBinder handle values? Should it evaluate them twice: once for determining the class should be instantiated once for setting the fields? Should it call the type converters twice? What if the request stream is non-buffered? What would be the exact condition to determine that no class should be instantiated? Value == null? Value == default(T)? What about empty string and null? + +I'm not saying this should be the ModelBinder default behaviour, I'm saying you could implement a ModelBinder yourself that creates the Address if there is some non-empty field in the form. Anyway this is Roslyn forum, not MVC. + +> > Well, it's impossible to add a new factory method to Expression class, a new value to ExpressionType enum, and libraries could have conflicts if they define their own `NullPropagationExpression`. It also looks like a tiny feature for taking a dependency an downloading a Nuget. But I think there's a third solution that we can explore. I will publish it in a moment in https://roslyn.codeplex.com/discussions/571077. +> +> The compiler does not bind to "the" Expression class. The compiler binds to "an" expression class that has static factory methods with the required signatures. You can write a perfect wrapper to System.Linq.Expressions.Expression with your custom extensions that forwards existing calls to the real Expression factories and the compiler will pick yours. +> Enums can have any value allowed by the underlying type. + +I'm quite sure you can not `wrap` an static class an expect that you're going to receive the calls... the callers will just get name conflicts. My new solution defines `ExpressionCSharp60` to give a chance to create the class in old frameworks. + + +--- + +On Nov 17, 2014 at 6:34 AM @dcumin39 wrote: + +Any reason we couldn't just have null propagation be the default behavior. Without the need to add ?. it could just take the default value of the value your are trying to access if anything is null. + +You could also make it so that ?. Would cause it to throw an error if anything was null. + +Excuse the brevity , I am writing this from my phone. + +--- + +On Nov 17, 2014 at 6:57 AM @PauloMorgado wrote: + +**dcumin39 wrote:** +> Any reason we couldn't just have null propagation be the default behavior. Without the need to add ?. it could just take the default value of the value your are trying to access if anything is null. +> +> You could also make it so that ?. Would cause it to throw an error if anything was null. +> +> Excuse the brevity , I am writing this from my phone. + +Yes. To much reasons to be listed here. + +For starters, when I write `instance.Member`, as a developer, I'm asserting that `instance` is not `null`. If it is, it's an exceptional situation and an exception should be thrown. + +The same reasons are behind the existence of `FirstAndDefault` and `SingleAndDefault`. + +You're free to try and use only `?.` instead of `.` and report back here if it really made things easy to you. + +--- + +On Nov 17, 2014 at 4:56 PM @Olmo wrote: + +**dcumin39 wrote:** +> Any reason we couldn't just have null propagation be the default behavior. Without the need to add ?. it could just take the default value of the value your are trying to access if anything is null. + +You mean in expression tres LINQ queries or in general? + +If you mean in general, I think your design will be a really big change in the semantics of the framework, and for worst. I think nullable reference types should be avoided at compile time, and since this is a big change, at least early detected at run-time by maiking . operator assert. Otherwise will be a really forgiving language like JavaScript. + +If you mean in expression trees LINQ queries, you have a point. I think the translation to SQL should be similar in most cases: just a LEFT OUTER JOIN, otherwise will be a breaking change, but you still have some benefits: + +- More clear: if you're navigating a Nullable navigatio property, use ?. and make it explicit. +- When using ?. with value types, they get automatically nullified. No strange cast required. +- Not all the expression trees are translated to SQL +- An brave LINQ provider could decide to translate ?. To LEFT OUTER JOIN, while . to INNER JOIN. + +> You could also make it so that ?. Would cause it to throw an error if anything was null. + +Well, if a feature like what you proposed will be added to JavaScript for example, I'll use !. instead. + +> Excuse the brevity , I am writing this from my phone. + + + +--- + +On Nov 17, 2014 at 5:07 PM @Halo_Four wrote: + +**Olmo wrote:** +> **dcumin39 wrote:** +> > Any reason we couldn't just have null propagation be the default behavior. Without the need to add ?. it could just take the default value of the value your are trying to access if anything is null. +> +> You mean in expression tres LINQ queries or in general? +> +> If you mean in general, I think your design will be a really big change in the semantics of the framework, and for worst. I think nullable reference types should be avoided at compile time, and since this is a big change, at least early detected at run-time by maiking . operator assert. Otherwise will be a really forgiving language like JavaScript. +> +> If you mean in expression trees LINQ queries, you have a point. I think the translation to SQL should be similar in most cases: just a LEFT OUTER JOIN, otherwise will be a breaking change, but you still have some benefits: +> +> - More clear: if you're navigating a Nullable navigatio property, use ?. and make it explicit. +> - When using ?. with value types, they get automatically nullified. No strange cast required. +> - Not all the expression trees are translated to SQL +> - An brave LINQ provider could decide to translate ?. To LEFT OUTER JOIN, while . to INNER JOIN. +> +> > You could also make it so that ?. Would cause it to throw an error if anything was null. +> +> Well, if a feature like what you proposed will be added to JavaScript for example, I'll use !. instead. +> +> > Excuse the brevity , I am writing this from my phone. + +JavaScript ain't that forgiving. Attempting to reference a property of a null reference still throws: + +``` +var z =x.y; // throws if x is null or undefined +``` + +--- + +On Nov 17, 2014 at 9:48 PM @Olmo wrote: + +True, I confuse it with how forgiving it is when the property is not defined (=set). + +Anyway, JavaScript is forgiving in many other senses (automatic conversions, semicolons, undefined properties, etc...), but you're right. + + + +--- + +On Nov 18, 2014 at 7:32 AM @PauloMorgado wrote: + +**Olmo wrote:** +> True, I confuse it with how forgiving it is when the property is not defined (=set). +> +> Anyway, JavaScript is forgiving in many other senses (automatic conversions, semicolons, undefined properties, etc...), but you're right. + +I consider javascript more deceiving than forgiving about semicolons. It tricks you into think you don't need them until you do. :) + +--- + +On Nov 18, 2014 at 8:45 PM @GabrielHorvath wrote: + +**dcumin39 wrote:** +> Any reason we couldn't just have null propagation be the default behavior. Without the need to add ?. it could just take the default value of the value your are trying to access if anything is null. + +It might make more sense to do so with Nullable/Option/Maybe values. The ? would convert a value/reference to a Nullable/Option/Maybe (empty when the reference is null) and in this case the . operator would map to a Select. In which case the following LINQ query for Option: + +``` +var r = from o in someObject.AsOption() + from s in o.GetPropertyValue("P") + from i in s.ParseToInt() + from v in i.Lookup(dict) + select v; +``` +could be written as: +``` +var r = someObject?.GetPropertyValue("P").ParseToInt().Lookup(dict); +``` +where r would of type Nullable/Option/Maybe. See [here](http://softwaretransactions.com/2014/11/07/empty-for-a-reason/) and [here](http://softwaretransactions.com/2014/10/13/combining-optionmaybe-and-ienumerable-in-linq-queries/) for implementation details of the LINQ query. + +But how would one distinguish between accessing a member of the Nullable/Option/Maybe struct and accessing the member of the contained value: Would a?.Value return _a_ or a.Value (assuming a is not null)? + + +--- + +On Nov 19, 2014 at 3:14 PM @dcumin39 wrote: + +I was talking to a coworker about this, who had some good thoughts. What if there was a base class that would make for automatic null traversal. This way it would only be the default behavior if explicitly stated. Either that or a class attribute that could be added that would make it the default behavior for that class but not necessarily any contained classes. + +--- + +On Nov 25, 2014 at 7:17 PM @Olmo wrote: + +I don't think inheritance will work here. I think null propagation could makes sense for any *nullable* members (or variables/parameters/return values). + +By *nullable* I mean any `Nullable`, `T` being an struct, or any value of reference type where nullable is allowed. Unfortunately in this case C# keeps this information implicit. + +Then, once a member type is nullable, sometimes it makes sense to propagate nullability, and sometimes to assert for non-null value. +* Maybe `person.DateOfBirth?.ToString() ?? "-- Unknonwn -- "` makes sense for reporting +* While `person.DateOfBirth.Value` is the way to go if you actually want to send a Birthday letter. + +Inheriting is a one shot thing, and here we need different weapons for different members in different contexts! + + +--- + +On Nov 25, 2014 at 10:57 PM @supercat wrote: + +**dcumin39 wrote:** +> I was talking to a coworker about this, who had some good thoughts. What if there was a base class that would make for automatic null traversal. This way it would only be the default behavior if explicitly stated. Either that or a class attribute that could be added that would make it the default behavior for that class but not necessarily any contained classes. + +I have long wished that .NET had defined an attribute that would specify that a non-virtual instance method must be invoked using "call" rather than "callvirt". If such an attribute had been defined, and C# honored it, then it would have been possible for `String` to define something like: + + [InvokableOnNull] bool IsNullOrEmpty() + { + return (this == null) || (this._Length == 0); + } + +and for code to safely say `if (someString.IsNullorEmpty()) ...` rather than `if (String.IsNullOrEmpty(someString) ...`. If types had included "get property or return default" members, then that would have greatly reduced the need for a `?.` operator. That obviously didn't happen, though; thus the need for `?.`. + +[Actually, even better might have been a means by which a type with a static method whose first parameter was a `ref` to the type could specify that the method should be invokable using a variation on member-referencing syntax, thus allowing code to do something like `myString.:Insert(3,"George");` which would insert "George" after the third character in `myString` and store the result back to `myString`. If the same syntax were used for accessing *struct* fields, then confusion about when something was acting upon a storage location, versus a heap object to which a storage location held a reference, could have been largely avoided. Too late now, though.] + +--- + +On Nov 25, 2014 at 11:01 PM @mirhagk wrote: + +You can already do something similar with extension method. If extension properties get made then this will be fully supported. + +Personally I'd rather not have to check the semantics of null reference exceptions with `.` notation for different classes. I'd rather write the one extra character. + +--- + +On Nov 27, 2014 at 6:53 PM @supercat wrote: + +**mirhagk wrote:** +> You can already do something similar with extension method. If extension properties get made then this will be fully supported. +> +> Personally I'd rather not have to check the semantics of null reference exceptions with `.` notation for different classes. I'd rather write the one extra character. + +I would have liked the `?.` concept better if it were an extension of the `? :` operator which basically said that if the second operand start with either `.` or an expression enclosed in `[]`, then evaluate the second operand if the first is non-null, or else evaluate the third (for conciseness, allow for `expr1 ? .member1 ? .member2 : elseExpr` to use `elseExpr` if either `expr1` or `expr1.member2` yields null). If e.g. `foo.bar` is supposed to yield a non-null string when `foo` is non-null, I think `foo?.bar : "No value"` would be semantically cleaner than `foo?.bar ?? "No value"` (if `foo.bar` does yield a null string when `foo` is non-null, the latter expression would mask the problem). That's not the concept that prevailed, however. + +--- + diff --git a/meetings/2014/LDM-2014-04-02.md b/meetings/2014/LDM-2014-04-02.md new file mode 100644 index 0000000..c6800be --- /dev/null +++ b/meetings/2014/LDM-2014-04-02.md @@ -0,0 +1,4820 @@ +# String interpolation +String interpolation is one of the top ten requests on uservoice - http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2312991-string-interpolations-in-c + +We've been discussing the feature back and forth in C# and VB language design meetings. This thread starts with a summary of the points raised from the Visual Basic Language Design Meeting of 2013-12-09, combined with comments from MVPs, but I'm hoping that everyone who's interested in the topic can add their thoughts and suggestions to the thread. (I switch back-and-forth between C# and VB syntax, which is how we did our discussions...) + +So far the consensus seems to be a resounding "meh" because the proposed designs seem to miss the mark, and the feature isn't as important as other potential new features. Here's a writeup of all our notes so far, so we can have a wider discussion with the general public. + +## Examples +Here are some examples in C# and VB: + +``` cs +var x = "It measures \{distance:0.00}m"; +``` +``` vb +Dim filepath = $"{drive}:\{path}\{file}.{ext}" +Dim query = $"{url}?arg1={x}&arg2={y}" +Dim csv_line = $"{name},{dollars}{vbCrLf}" +``` + +String interpolation would be syntactic sugar for a call to String.Format: + +``` cs +var x = "It measures \{distance:0.00}m"; +==> +var x = String.Format("It measures {0:00}m ", distance); +``` + +## Motivation + +Why string interpolation? Isn't String.Format already good enough? More generally, what is the real problem that string-interpolation tries to solve? Here are some possible answers, both good and bad. I think we haven’t yet got a crisp good answer to this question. Some possible answers: + +1. It is a full replacement from String.Format, with culture and everything, to solve the mess that you find when there are too many positional arguments (see examples below) +2. It provides a quick-and-dirty way to do string concatenation in the invariant-culture for use in composing strings together to pass to other APIs +3. It provides an easy way to stick in identifiers +4. It is an easier way to teach Coding 101 – don’t want to start beginners on String.Format +5. All the other cool languages have it. (bad reason) + + +## Motive 1: a replacement for String.Format +Proposal: "String interpolation is a replacement for String.Format, for cases where the "positional" arguments {…} of String.Format are confusing to read." As a motivating example, the following code has a few bugs (missing space before {5}, and the orders have got mixed up) and it's not really human-readable… + +``` vb +Dim x = String.Format("Robot {1} reporting{0} {3} levels are {2}{0}" & + "{5} levels are {4}", + vbCrLf, name, coolant.name, coolant.level, + reactor.name, reactor.level) +``` + +Isn't normal string concatenation already good enough? Again, sometimes it's confusing. The bug here is that there's a missing vbCrLf at the end of the second line: + +``` vb +Dim z = "Robot " & name & " reporting " & vbCrLf & + " " & coolant.name & "levels are " & coolant.value & + " " & reactor.name & " levels are " & reactor.value +``` + +One curious proposal is to allow string concatenation just by omitting the & in VB (or + in C#), just as is currently the case in C for string literals. But this seems pretty limited -- wouldn't allow for format specifiers, would only work for expressions with string literals before and after them, might close the language off to context-specific keywords in the future, and doesn't look very readable. + + +String interpolation could be neater and easier to debug than either String.Format or concatenation... +``` vb + Dim y = $"Robot {name} reporting +{coolant.name} levels are {coolant.level} +{reactor.name} levels are {reactor.level}" +``` + +However, this example is fishy. Most professional programmers won't be writing user-facing strings in code. Instead they'll be storing those strings in resources (.resw, .resx or .xlf) for reasons of localization. So there doesn't seem much use for string interpolation here. + + +## Motive 2: a quick-and-dirty way to do construct strings for APIs. +``` vb +Dim filepath = $"{drive}:\{path}\{file}.{ext}" +Dim query = $"{url}?arg1={x}&arg2={y}" +Dim csv_line = $"{name},{dollars}{vbCrLf}" +``` + +These examples seem to work nicely with string interpolation, but we'd be opening up potential security holes when people construct strings without sanitizing them first. That's a worry. + +Some open questions about string interpolation... + +## Q. Default culture? +What should the default culture be for string interpolation? +* String.Format uses CultureInfo.CurrentCulture by default +* It feels like InvariantCulture would be a better bet if users are typically using string-interpolation as a way to construct strings to pass to APIs. +* CurrentCulture would be make more sense only if (1) people use it mainly for UI strings, (2) and they’re not localizing their UI by storing strings in .resw +* CurrentCulture also makes sense if the feature feels really close to String.Format. + +## Q. Specify culture? +Should users be able to specify the culture to be used? +* The Javascript proposal (next post) would allow this, since you could call +``` vb +Dim s = FmtInvariantCulture`hello {x}` +Dim s = FmtCurrentCulture`hello {x}` +``` +* If we want to use a prefix for string interpolation `var x = $"hello";`, then that provides a place where culture could be specified `var x = ${culture}"hello";` +* Alternatively, culture could be specified at the end, e.g. +``` cs +var x = "hello \{x}"(CultureInfo.CurrentCulture); +``` +``` vb +Dim x = $"hello {x}"(CultureInfo.CurrentCulture) +``` +* MVP Bill McCarthy suggested this interesting syntax: +``` vb +Dim x = $"hello {x}" Using CultureInfo.CurrentCulture +``` + +## Q. Perf of String.Format ? +What kind of compiler optimizations would be used? +* If the compiler sees that it can optimize away the call to String.Format, and use instead something more lightweight (that still has the same semantics), then it would: +``` cs +var x = "It measures \{distance}m"; +==> var x = String.Format("It measures {0}m ", distance); +==> var x = String.Concat("It measures ", distance.ToString(), "m"); +``` +* Will there be compiler folding? e.g. +``` cs +var x = "hello \{a}" + "world \{b}"; +==> var x = "hello \{a}world \{b}"; +==> var x = String.Format("hello {0}world {1}", a, b); +``` +* Will the compiler do compile-time evaluation if the arguments that go in the hole are constant and which don’t depend on the current culture? It seems risky to take a dependency on the internal behavior of String.Format… + +## Q. Prefix syntax? +On an interpolated string, should it have a prefix? +``` cs +var x = "It measures \{d}m"; +var x = $"It measures {d}m"; +``` +* For C# we were leaning towards the first form, where the presence of `\{.}` inside a string implies it is to be interpolated. For VB that's impossible, so we were pushed towards the second form where the prefix implies that the string will be interpolated. +* MVPs raised the issue that, if you wanted to do a global search to find all interpolated strings in a codebase, the prefix form makes it easier. If you merely searched for \{ then you'd get false-positives from double-escaped \\{, and from \{ in XML doc-comments. +* For choice of prefix, we considered the following, and preferred $ +``` vb +Dim x = @"hello" ' bad, since confusing with C# verbatim strings +Dim x = #"hello" ' used in a few other languages +Dim x = $"hello" ' used in Nemerle. Fits with the VB heritage +``` + +## Q. Hole syntax? +In an interpolated string, what syntax to use for marking an expression hole? And then how do you escape that syntax if you want to use it literally? For VB we considered the following: +``` vb +Dim brace = $"hello {name} "" {{ {vbCrLf}" ' PHP, Python, String.Format +Dim escape = $"hello \{name} \" \\ \n" ' C# proposal +Dim hash = $"hello #{name} "" ## ${vbCrLf}" ' Ruby, Coffeescript +Dim $brace = $"hello ${name} "" $$ ${vbCrLf}" ' Dart +Dim $paren = $"hello $(name) "" $$ $(vbCrLf)" ' Nemerle +Dim $dollar= $"hello $name "" $$ $vbCrLf" ' PHP, Perl, Dart, Nemerle +``` +* `brace` is strongly reminiscent of String.Format, which is good. And {{ is how you already escape out of String.Format. The "vbCrLf" is reasonable way to get a newline. We preferred this option for VB. +* `escape` has the nice feature that it opens the door for escape characters, and is like C#. But it feels wrong to have to have that redundant extra character. +* `hash` doesn’t have much going for it. +* `$brace` re-uses $ in a reasonable way but the way to escape $ is pretty ugly +* `$paren` is a Nemerle variant of `$brace` with the same disadvantages +* `$dollar` would be a nice shortcut for the common case of just wanting to include an identifier, and is something allowed by string-interpolation in a lot of other languages. However it only combines well with `$brace` and `$paren`. + + +## Q. Expressions in holes? +What kinds of expressions should be allowed in the "holes"? Just identifiers? Or qualified identifiers? How about indexers? Invocations? Arbitrary expressions? +``` cs +var x = "This is \{arg[0]} and \{arg[1]}"; +var y = "This is \{Lookup(x)} "; +var x = "Welcome \{user.Name} to \{user.Activity}"; +``` +* MVPs justifiably observed that it would be bad practice to use complex expressions inside the holes. +* But it's hard to draw a clear compiler-enforced line between "things that are okay in holes" and "things that aren't". For instance, indexers and method-invocations are probably good. So to keep things plain and regular, the compiler should probably to allow all expressions. +* There's a good precedent for this. With the async/await feature, we wanted to allow things like `var x = await t` and also `using (var x = await t)`. And so we allowed await in all expression contexts, even though it's bad practice to use them in confusing places like `x[await t1] += await t2;` +* In practical terms, it's very hard (in terms of implementing the lexer+parser) to allow arbitrary expressions inside a hole. VB already does this for expression holes in XML-literals, so could use the same codebase for expressions in interpolated-string-holes. But C# doesn't yet do this anywhere. +* Should it allow format-specifiers like the :0.00 used in the first example? That might complicate the lexing/parsing. But we don't see any ambiguity problems. + + +## Q. C# verbatim string? +Can you use string interpolation in a C# verbatim string? + + +## Q. VB implicit line continuations? +How does it work with VB implicit line continuations? e.g. +``` vb +Dim x = $"hello { 3 + + 4 } world" +``` +Answer: it works exactly the same as always: implicit line continuations happen before or after specific characters. The fact of being inside a hole in an interpolated string doesn’t change that. So the above code won’t work, since implicit line continuation isn't allowed before +. + +Also, { allows an implicit line-continuation after it as always, and } at the end of a hole allows an implicit line continuation before it as always. Note for precedent that `Dim x = New With { .x=15}` also allows an implicit LC after the `{`, and XML expression holes allow an implicit LC after the `<%=`. + +## Q. VB Case statement? +Can you use interpolated strings as a VB Case expression? e.g. `Select Case x : Case $"hello {a}` +Answer: Yes. VB case statements always allow computed values here. C# never does. + +## Q. Const? +Is an interpolated string const? `Const x as String = $"hello"` + +Answer: No, never, not even for the ones that are clearly recognizable as constants. The rule is that string interpolation is semantically shorthand for String.Format (notwithstanding any under-the-hood compiler optimizations). + +## The Python alternative +There's an idiom used in Python today, that some people in VB/C# are also adopting. Here's how it would look in .NET: + +``` vb +Dim args = New With {.Name = "Jones", .Address = "1256 92nd St"} +Dim str = "Mr {Name} lives at {Address}".FormatEx(args) +``` + +The idea here is that it's all the positional format-arguments {0}, {1} in String.Format that are difficult to read. So we could do them using named arguments {Name}, {Address}. You would write an extension method on String which takes an object. At runtime your method parses the string to find curly braces (just like String.Format does), and it uses reflection to look for corresponding members in the "args" object. + +(You'd have to be careful that the folks doing localization of your strings don't try to localize {Name} or {Address} !) + +With the proposed "dictionary" features of C# and VB vNext, you could write FormatEx using dictionary arguments rather than reflection: +``` vb +Dim args = New Args With {!Name = "Jones", !Address = "1256 92nd St"} +Dim str = "Mr {Name} lives at {Address}".FormatEx(args) +``` + +This Python-style approach would also solve the culture problem -- because you can easily pass Culture to your extension method. + +## The Javascript alternative +There's a string-interpolation proposal on the table for the next version of EcmaScript that's designed specifically to solve the security worries of string interpolation. It also solves Culture. +http://wiki.ecmascript.org/doku.php?id=harmony:quasis + +It would look something like this if we translated it into .NET: +``` vb +Dim x = c.Fmt`Mr {name} at {address}` +==> +Dim x = c.Fmt({"Mr ", " at ", ""}, name, address) +``` +The idea here is that at compile-time the interpolated string gets broken up into pieces, and then at runtime it gets passed as an argument to the specified function or delegate. The advantage, as compared to the Python approach, is that there's less runtime overhead. + + + +We had discussed something sort-of-related for C#. "There are many different APIs which work the same way: let's come up with a proposal for string-interpolation which works with all of them". It would expand an interpolated string into a *comma-separate sequence of expressions* : +``` vb +$"hello {a} in {b}" +==> +"hello {0} in {1}", a, b +``` + +This would enable it to be used for a wide variety of APIs, e.g. +``` vb +1: Console.WriteLine($"hello {a} in {b}") +2: Dim x = String.Format($"hello {a} in {b}") +3: Diagnostics.WriteLine($"hello {a} in {b}") +``` +However it would prevent another idiom, +``` vb +4: Dim x = $"hello {a} in {b}" +``` + +The pain of losing [4] is severe. And the other cases above all boil down to a call to String.Format, so we're not losing much by saying that string-interpolation is always translated into a call to String.Format. Indeed, we think that most of the time people take parameters `(fmt as string, ParamArray args as Object())`, then they just pass it down straight to String.Format. True there are just a few cases where they don't... for instance, the Roslyn codebase has a function where it modifies the format and args before passing them to String.Format. The pain of losing these scenarios seemed less than the pain of losing [4]. + + +--- +--- + +On Apr 3, 2014 at 6:43 PM @Vincent3000 wrote: + +I cannot catch what's wrong with "interpolation string" (InStr) and why I read all that blah-blah of strange arguments. +InStr is quite simple and doesn't require to wrinkle your forehead. +1. InStr IS NOT a replacement or sugar for string.Format! It's just another feature to quickly insert values into string. String.Format is just ugly, verbose and tricky for such simple task. +2. Since main goal of InStr is simplification of operation, it should look same simple. So Nemerle-like syntax is the best: $"My name is $name!". If you're not scary to go further, quotes also can be more distinguishable, say 'backquotes': var s = `name=$name`; +Escape sign? Same dollar! `My salary is $$$salary`. For complex (and rare) cases {} are the limit: `Name = ${person.FirstName}`. +Related culture and other sh*t most of us never worry about: FORGET. Just make 'this_piece_of_data.ToString()' and put it into my string. Period. +So how long we will piss around for things other languages have from the box? +We're NOT building an 'ideal language' (esp. when C# is already far from it), we just need PRACTICAL FEATURE. If you write real code (instead of writing language bells-and-whistles), you meet this string interpolation in billion places! That's why people ask for implementing feature and not discussing "oh, it looks like string.Format!". + +So when this goddamn simple thing will appear? + +--- + +On Apr 4, 2014 at 4:26 AM @lwischik wrote: + +Hey Vincent3000, you're right, it's important to always remember that the "bread-and-butter" simple common cases have to remain simple. Yours is a strong vote for the Nemerle $. + +For the semantics, if you're just calling ToString(), that's exactly what String.Format does! ToString() uses the current culture. So what you've described _has exactly the same effect_ as syntactic sugar for String.Format in the current culture, but with one differences... +* String.Format works when an argument is null (it uses an empty string). Do you mean you'd prefer to throw a NullReferenceException for "$name" when name is null? + +... + +On a more general note we’d appreciate it if posters take into consideration that this forum is read by people of all backgrounds. Please everyone ensure your posts are polite and contain no inflammatory or aggressive language that some readers may find offensive. Thanks! + + +--- + +On Apr 4, 2014 at 12:49 PM @AdamSpeight2008 wrote: + +Are you aware an implementation (sort of) already exists. [See Here](http://www.hanselman.com/blog/ASmarterOrPureEvilToStringWithExtensionMethods.aspx) + +--- + +On Apr 4, 2014 at 3:11 PM @styx31 wrote: + +There is a .net library project [serilog](http://serilog.net/), which implements some kind of string interpolation to allow to store message (formats) in some kind of resource dictionary (think of event log message string resources) and execute them againt a collection of objects. + +It does a lot of things (enumerations join, force stringification, object dump), that are certainly out of scope for this feature, but perhaps there are some interesting goals and ideas that could deserve a read. + +--- + +On Apr 5, 2014 at 10:34 AM @ashmind wrote: + +I must say that this a very important feature in my opinion. + +I think "most professional programmers won't be writing user-facing strings in code" is missing the point -- there are a lot of cases when similar examples are OK: +1. Any logging. I can't count all the times I used Info/Debug/etc that all use string.Format internally. It is very easy to make a mistake in the code, and while tests help it is hardly perfect, especially when someone writes LogFatal and later that does not log because there is {2} instead of {1}. +2. Exceptions -- it is often useful to have detailed exception messages, however user does not normally see those, so unless it is a framework or a very popular library there is little reason to localize them. +3. Non-enterprise code. Minor libraries, tools and other (often OSS) projects may not consider localization within their scope. +4. Enterprise non-user-facing code -- in places where I worked there was a lot of internal dev tools and utilities that did not require localization. + +And here are some more examples that benefit from more advanced interpolation scenarios (ES-like): +1. Serilog, as mentioned by styx31 -- it could get proper arguments instead of having to analyze string dynamically. +2. SQL -- ES-like implementation would allow arguments to be analyzed/passed as command parameters. + +I would love ES-like solution (with better syntax though), but even a straightforward solution would be really good. + +--- + +On Apr 5, 2014 at 12:02 PM @svick wrote: + +**AdamSpeight2008 wrote:** +> Are you aware an implementation (sort of) already exists. [See Here](http://www.hanselman.com/blog/ASmarterOrPureEvilToStringWithExtensionMethods.aspx) + + +That's not quite the same. Scott Hanselman's code is limited to interpolating properties of a single object. + +This proposed feature would allow you to interpolate on things like local variables, their properties or results of functions (depending on what answer is selected for the Expressions in holes? question above). + +--- + +On Apr 5, 2014 at 2:05 PM @ashmind wrote: + +Some opinions: +1. Allowing all expressions in the hole seems to be the best solution for a language. Some people may misuse that, but that's true for any feature. +2. Format specifiers seem like a good idea as long as ambiguity is not a problem. Should it be an error if the expression is not statically IFormattable or has no ToString(string) overload? +3. Instead of allowing this in @ strings, would it be possible to allow multiline in standard strings? + +For ES-like solution, would it be a good idea to have something like +``` +public class InterpolatedString { + public string Template { get; } /* string.Format compatible template */ + public object[] Values { get; } /* values passed into holes */ + public string ToString() /* default implementation, does string.Format */ +} +``` +? + +Then you can generate it based on the context, same as with Expression. +This would play really nice with libraries (and extension methods?), but I am not sure if that is too complex for things like overload resolution. + +--- + +On Apr 5, 2014 at 3:03 PM @Timwi wrote: + +When I look at the following two alternatives: + +``` +"My name is " + Name + " and I am " + age + " years old." +"My name is \{Name} and I am \{age} years old." +``` + +I notice the first one already allows arbitrary expressions in holes while the second one probably has problems with expressions containing literal strings or block lambdas. + +It seems to me that whatever approach is taken, the hole should either explicitly *not* accept arbitrary expressions, or the arbitrary expression should be outside of an enclosing syntax element such as double-quotes or curly brackets. + +Something like the following might be conceivable: + +``` +"My name is "{Name ?? ""}" and the youngest in my team is "{team.Min(p => { return p.Age; })}" years old." +``` + +with the idea here being that from the point of view of lexing, the contained expression is outside of the literal string, while a grammar rule would cause the parser to reconstruct the complete string for the compiler to generate a call to `string.Format`. That way the lexer would no longer need to support arbitrary nesting. + +(Please do not comment to tell me that the lambda expression can be written without curlies; I am well aware of that. It’s an example and it’s valid code that would have to work.) + +If you *do* want to allow arbitrary expressions inside `\{ ... }`, the lexing rules would get complex. In the expression + +``` +"My name is \{Name ?? ""} and the youngest in my team is \{team.Min(p => { return p.Age; })} years old." +``` + +the lexing rules would have to *either*: +* recognise that `""` is a nested string rather than the end of this token followed by a less-than operator (requires keeping track of arbitrarily nested double-quotes); *or* +* treat `\{` as the end of this token and lex the containing expression normally, but then recognise `}` as the beginning of the next string token (requires keeping track of arbitrarily nested curlies); *or* +* lex the entire string literal as a literal (which would require it to take account of nested quotes and curlies too) and then have the parser reinvoke the lexer to re-lex the contained expression; *or* +* the language feature would have to disallow nested string literals and curlies entirely. This option would be annoying to the programmer, especially given that the claim to supporting *arbitrary expressions* is now no longer true. + +This seems not worth it considering that the previous alternative (`"My name is "{Name ?? ""}" and..."`) is only one character longer in code and vastly more straight-forward to lex. There is no need for the lexer to take care of any nesting, and the grammar reduces `( ('{' '}')*)* opt` to a non-terminal that the compiler transforms into a call to `string.Format`. + +--- + +On Apr 6, 2014 at 11:20 AM @Necroman wrote: + +My opinion about string interpolation is based on the KISS principle "Keep is simple, stupid". It should not attempt to be full-featured replacement of string.Format, but rather lightweight string.Format alternative with similar syntax for composing strings. + +Q. Default culture? +* CurrentCulture as the default - it won't break the result when user starts refactoring string.Format expressions! +* There really should be an option to specify the culture. The last proposal with 'using' seems fine to me. +* Or maybe like this using culture in the last braces +``` +string str = $"Today is: {DateTime.Now}{Culture=CultureInfo.InvariantCulture}"; +``` + +Q. Prefix syntax +* definitely with prefix. We don't want all existing strings containing braces to start behaving like interpolated string out of the blue. +* $ looks good to me, no confusion with verbatim strings, easy to remember + +Q. Hole syntax +* I see no problem with the first "brace" syntax - there is no backslash clutter and the fact the string starts with $ gives user immediate information that braces have special meaning here. It's also very similar to string.Format syntax which is quite useful. +* Alternatively we could take a look on the [Razor syntax](http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx/), it uses only single character to denote simple expressions, but it is something not usual for developers, that know how string.Format works. + +Q. Expression in holes +* I'd recommend to take inspiration in [Path syntax in XAML Binding](http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj569302.aspx). +* The Path property can be either single property, or traversal to nested property, or indexer as well. +``` +Text="{Binding MyModel.Cars[1].IsStarted}" +string str = $"The second car is started: {MyModel.Cars[1].IsStarted}"; +``` +* It's not allowed to use invocations in XAML for good reasons - this could lead to overly complicated solutions like interpolated string as an argument of invocation inside interpolated string, etc. I'm not saying it cannot be done, but it will be quite an overkill that we don't want. +* In XAML if the expression contains nulls or other Exceptions, it's silently ignored. In string.Format if it's just a simple null value, it's ignored, but any Exception when evaluating more complex expression is thrown. We should probably follow similar behavior to string.Format when evaluating nulls and Exceptions. +* yes for format specifiers. + +Q. Verbatim string +* I think Interpolated strings should work as a feature superset of verbatim strings, so no string interpolations inside regular verbatim strings. + +Question to think about - option to interpolate plain strings? +* Lets say I have simple string, that I want to interpolate on other place, typical scenario: AppResources. How to do that? +``` +// what about like this +string format = localizedStrings.StartedCarFormat; +// == "The second car is started: {MyModel.Cars[1].IsStarted}" +string localizedString = $format; +``` +* Should we have the option to use string from resources, that will be later interpolated in the app? + +Summary: +* Interpolated strings should be lightweight alternative of string.Format, based mostly on string.Format syntax, with expressions in holes based on Binding.Path syntax. + +--- + +On Apr 6, 2014 at 5:20 PM @mirhagk wrote: + +My vote is for using a prefix so existing strings aren't affected, and not allowing it in verbatim strings. Most of the time I use verbatim strings because I'm writing a regex and hate escaping things then re-escaping them. Also for the same reason, existing verbatim strings shouldn't be affected. + +Perhaps there's some merit to supporting `string str = $@"verbatim interpolated string {x}"` but I personally don't see a lot of use cases for it. + +I think it should compile to calls of string format, with the expressions replaced with numbers as mentioned. This means converting existing cases of `string.Format` should be very straightforward. + +String interpolation does concern me a bit with the potential developers may write code like the classic PHP: +~~~ +$pass = mysql_query("select password from dbo.users where username = '$_GET["username"]' ") +~~~ + +But the benefits with log messages, and quick messages for non-enterprise applications is pretty big. + +Removing the ability to do arbitrary expressions inside the interpolation would mean the above PHP code wouldn't be as easy to write, but it also would make lots of other perfectly valid code difficult to write. If indexers and property accessors are allowed, then function calls should be, so at least the programmer can write `$(Escape(Request.Form["username"]))` and it'd still scare me a bit, but at least it's a bit safer. + +--- + +On Apr 6, 2014 at 6:16 PM @Vincent3000 wrote: + +**lwischik wrote:** +> For the semantics, if you're just calling ToString(), that's exactly what String.Format does! + +As I said, string.Format is not an option - InStr can _behave_ similar, but it's separate feature which can "beautify" code and make it less error prone. + +> * String.Format works when an argument is null + +When argument is null, InStr will just skip it. + +> Please everyone ensure your posts are polite and contain no inflammatory or aggressive language + +Sorry, agree. My emotions so high because I know this feature 'costs' 10 lines of code, while discussion of this feature already took thousand! How it can be so irrational? I think "D way" could be useful here: first, immediately implement feature by some draft. Second, give it to people for real use. Read feedback. Fix issues and improve syntax/logic/etc. Return to step 2. This way we can reach ideal way faster than drawing circles in clouds. + + + +--- + +On Apr 7, 2014 at 1:37 AM @mirhagk wrote: + +I actually agree with Vincent3000 on the implement first, discuss later approach. + +Now that Roslyn is open source, I'd love to see an experimental version of the language be released every so often with new features implemented the way the core team thinks works best. Then let the community play with it, and see if what they think. If someone uses a feature, and it acts differently then they expected, then discussion should be brought up. At that point, any features that has been settled on can be put into a real release, with any that are still in discussion remaining in the playground. + +That being said, now that Roslyn is open source, this doesn't even require the work of the Roslyn team to do so. I don't consider myself experienced enough to really contribute in a huge way, but I'm going to try to learn the code base, and hope that others will join me. We can implement features in forks that we think work, and others can provide feedback. If the community has an implementation, and agrees that it works, then it shouldn't take as much work for the core Roslyn team to go over the implementation, fix any problems they notice, and bring into the core implementation. + +Releasing Roslyn as a nuget package was a really smart decision, because it means beta features can be released through there, allowing teams to turn on or off the beta features as required. + +--- + +On Apr 7, 2014 at 6:57 PM @VladD wrote: + +From the localization point of view, it would be of an advantage to be able to convert a normal string into an interpolated string -- like `string.Format` does. + +Use case: a developer creates a vanilla string in "Developer English": +`"Do you really want to delete the file {filename}?"` + +It gets translated into other languages: +`"Sind Sie sicher, dass Sie die Datei {filename} wirklich löschen wollen?"` +`"Вам совершенно точно больше не нужен файл {filename}? Подумайте."` +etc. + +Now, the developer has to substitute `{filename}` with the real variable content in the code. So we need a library method `string.Interpolate(string format, object context)` or the like, so that the substitution would look like `var translated = string.Interpolate(template, new { filename = filename });`. + +Edit: Oh, I see this problem has been already raised in Necroman's comment. + +--- + +On Apr 7, 2014 at 11:23 PM @PauloMorgado wrote: + +# Cultures + +Being first and foremost intended for building API strings, I would say any culture dependent culture (:)) is out of the question. The best bet is **InvariantCulture**. + +Allowing the specification of the culture would become very cumbersome and would defeat the whole purpose of the feature. + +# Prefix vs. Hole Syntax + +I really hate the \{ proposed for C# instead of a prefix. I can't see a reason for that. + +Why not using **#** or **$** as a prefix for interpolated strings. For **C#**, this prefix could even be combined with the verbatim string prefix (**@**). + +# Verbatim Strings + +Why not? + +Really! Of course it should be allowed. Unless there's a very compelling reason for not to. + +# Expressions In Holes + +DevOps is becoming more and more common. This will require many developers to write scripts in scripting languages like **Powershell** which allows expressions in interpolated strings. Why not allowing it also in **C#**/**VB**? + +# Format Specifiers + +Of course they should be allowed. + +# Constants + +Only if the compiler is able to generate compliable code for the constants. That would mean that for some scenarios this feature wouldn't lead to a call to **string.Format**. + + + +--- + +On Apr 8, 2014 at 1:10 PM @mirhagk wrote: + +> Really! Of course it should be allowed. Unless there's a very compelling reason for not to. + +I'd say allow a combination `$@"verbatim {interpolated} string"`, but do not pollute existing ones, no matter what you do. Verbatim strings are used in regular expressions a lot, and having to escape anything in a verbatim string would be very annoying. + +--- + +On Apr 9, 2014 at 10:42 PM @marcinjuraszek wrote: + +**mirhagk wrote:** +> We can implement features in forks that we think work, and others can provide feedback. If the community has an implementation, and agrees that it works, then it shouldn't take as much work for the core Roslyn team to go over the implementation, fix any problems they notice, and bring into the core implementation. +> + +I agree with that. However, even in open source world you can't force C# Team to accept a pull request. That's why I think it's good to discuss things first. Project owner might save you a lot of work saying that it's not gonna make it into Main before you start working on particular feature. + +Anyway, who's going to implement community-version of string interpolation? :) + +--- + +On Apr 9, 2014 at 10:48 PM @lwischik wrote: + +**marcinjuraszek wrote:** +> However, even in open source world you can't force C# Team to accept a pull request. That's why I think it's good to discuss things first. + +The way I see it, the job of a prototype is to uncover issues that you hadn't even thought of. In this case, there are a lot of issues to answer first that we're _already_ aware of! + + +--- + +On Apr 19, 2014 at 2:22 AM @davedev wrote: + +Hi, + +The first thing I thought about was how useful this feature would be when writing exception messages. I'd probably write them more often and more verbosely, if I had this feature, I thought. (Barring of course exceptions that require localization due to their being Framework APIs.) I probably wouldn't use it in many other places though. + +```c# +public double DivideByDistance(double value, Point point1, Point point2) +{ + Vector vector = point - point2; + var distance = vector.Length; + + if (distance == 0) + { + throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}. Division by zero is not permitted."); + } + + return value / distance; +} +``` + +\- Dave + +--- + +On Apr 28, 2014 at 1:02 PM @McZosch wrote: + +String Interpolation is no language feature, IMO. + +I have a triplet of extension methods covering almost any case mentioned in this post with 26 LOC. First, a [Format]-method taking a delegate, second a IDictionary-variant calling the first using a lambda, third a Object-variant calling the first using a lamba. Also handles conversion of IConvertibles through using CurrentCulture. Very much the same as the "Python-way" explained above. + +One of the things I didn't find here was the problem of leading or trailing characters after null-values, resulting in redundant characters. + +``` +Dim s = "There is a {building} in {city}, {state}" +'If state is null +Dim r = "There is a house in New Orleans, " +``` +Clearly, the comma and the trailing blank are redundant. I solve this by writing: +``` +Dim s = "There is a {building} in {{city}, {state}}" +``` + +This feature-set is based on elementary string-parsing; here are the internals: + +``` +Dim s = "There is a {building} in {{city}, {state}}" +Dim r = s.Ranges() 'Returns an IEnumerable as follows: {"There", " ", "is", " ", "a", " ", "{", "building", "}", " ", ....} +Dim i = r.Tokens() 'Returns an IEnumerable as follows: {"There is a ", "{building}", " in ", "{{city}, {state}}"} +Dim t = i.Select(function(x) if x.IsToken then .... else .... end if) 'outer replace; the terminal outer bracket is simply handled as a recursion +Dim a = t.Join(String.Empty) 'Output; Join being the simplest choice +``` + +The easiest call is: + +``` +Dim s = "There is a {building} in {{city}, {state}}".Tokenize(New With {.building = "house", .city = "New Orleans", .state = "Louisiana"}) +``` + +Ooops, forgot something; evaluating on-demand values through lambdas is equally possible: + +``` +Dim s = "There is a {building} in {{city}, {state}}".Tokenize(New With {.building = "house", .city = "New Orleans", .state = function() GetStateOf(.city)}) +``` + +A planned addition to add annotations, to enforce uppercase or lowercase has not yet materialized. + + +--- + +On May 1, 2014 at 9:03 AM @Necroman wrote: + +Here's an idea when designing the String Interpolation language feature - there is already quite similar feature in .NET - the __DebuggerDisplay attribute__: +http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx + +``` +[DebuggerDisplay("Student: {FirstName} {LastName}")] +public sealed class Student { + public string FirstName { get; set; } + public string LastName { get; set; } +} +``` + +It allows to __customize the displayed information when debugging your app__. It even support almost any Expressions in curly braces like logical operators, calling functions, etc. +I'm not saying Interpolated strings should be implemented in the exact way, but it's definitely a nice starting point for designing this new language feature. + +--- + +On May 1, 2014 at 5:40 PM @PauloMorgado wrote: + +AAARGHH!!!! + +`DebuggerDisplayAttribute` doesn't use `\` before the `{`s for interpolation. + +--- + +On May 3, 2014 at 7:58 PM @xor88 wrote: + +Whatever the feature will look like it should probably map 1:1 to string.Format. This keeps the concept count low. We can just tell people: string interpolation is this C# 6 cool thing that makes string.Format easier. Everyone gets that. No junior devs make mistakes due to slight deviations in behavior. + +Same semantics (except of course the different hole syntax). Culture support. Support for custom format strings. + +The C# compiler should of course optimize the computation because many common cases will map to a very fast call to string.Concat. + +That said, how many places does a typical code-base have that require string formatting?! __This feature is a candidate to be dropped. Please use your time for something else!__ + + +One additional idea that stems from two use cases: + +1. I want to specify a culture +2. I want to HTML-encode holes + +Let's give the programmer a way to specify a custom formatter. I could say: + +``` +$"hello {name:System.Globalization.InvariantCultureFormatter}!" +``` + +and this would statically bind to the expression System.Globalization.InvariantCultureFormatter.ConvertToString(name). Or: + +``` +$"hello {name:System.Web.HtmlEncodeFormatter}!" +``` + +It would be useful to specify the formatter for the whole string, of course: + +``` +$"hello {name}!" using HtmlEncodeFormatter +``` + +Making outputting HTML trivial. Imagine the alternative: + +``` +"hello " + HttpUtility.HtmlEncode(name) + "!" +``` + +This is much worse. + +In these examples I have assumed that the formatter types provide a static method that would convert anything (subject to normal method overloading rules!) to a string. This is an idea that I want to contribute because previously I have only seen examples where someone passed in an instance of IFormatProvider. By using static methods and overload resolution we could increase performance. + +Note, that with "static using" specifying a culture as an object reference would not be too bad either: + +``` +using CultureInfo; +//... +$"hello {name:CurrentCulture}!" +``` + +Resharper would surely offer help adding that using statement. This would be very low friction. + + + +So what's the default culture/formatter. I'd like the feature to have none. It *must* be specified. This is to enforce code quality in a junior team. + +On the other hand this might be an impractical barrier of entry. If you *must* have a default formatter use the same that string.Format uses: CurrentCulture. As horrible as this is I think it's the right choice. + + + +--- + +On May 3, 2014 at 10:15 PM @supercat wrote: + +**xor88 wrote:** +> That said, how many places does a typical code-base have that require string formatting?! __This feature is a candidate to be dropped. Please use your time for something else!__ + +I can see many uses for `String.Format` and `StringBuilder.AppendFormat` overloads which provide the ability to access a member of a passed-in object as part of processing; from a format-specification standpoint, something like `{3.X:0.00}` would access member X from a argument 0 and format it as "0.00"` [the behavior would probably be compatible with `String.Format`, but adding the ability to invoke arbitrary members of the passed-in arguments to existing usages might pose a security hole]. I'm not quite clear I see as many uses for having a feature which is in some regards more versatile, but would be limited to literal format strings. + +--- + +On May 3, 2014 at 10:33 PM @supercat wrote: + +**davedev wrote:** +> throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}. Division by zero is not permitted."); + +How much better is that, really, than + + throw new ArgumentException("The specified value {0} cannot be divided by the distance of two equal points, {1} and {2}. " + "Division by zero is not permitted.".FormatWith(value, point1, point2) ); + +How will each approach react if code is reformatted to rename `point1` to `leftPoint`? + + + +--- + +On May 3, 2014 at 11:28 PM @PauloMorgado wrote: + +**supercat wrote:** +> I can see many uses for `String.Format` and `StringBuilder.AppendFormat` overloads which provide the ability to access a member of a passed-in object as part of processing; from a format-specification standpoint, something like `{3.X:0.00}` + +That would be nice with short circuiting semantings as in most data binding thechnologies. But that would be something for the BCL, not the language. + +--- + +On May 3, 2014 at 11:29 PM @PauloMorgado wrote: + +**supercat wrote:** +> **davedev wrote:** +> > throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}. Division by zero is not permitted."); +> +> How much better is that, really, than +> +> throw new ArgumentException("The specified value {0} cannot be divided by the distance of two equal points, {1} and {2}. " +> "Division by zero is not permitted.".FormatWith(value, point1, point2) ); +> +> How will each approach react if code is reformatted to rename `point1` to `leftPoint`? + +Easier to read and understand, specially when the text is long and/or there are lots of arguments. + +--- + +On May 4, 2014 at 2:57 PM @xor88 wrote: + +I just realized that we already have a way to add culture and encoding to string interpolation. We can call methods as part of the string: + +``` +var temp = 0.0; +"Temperature: \{Format(temp, "F2", CurrentCulture)}" +``` + +This would require somehow bringing a "Format" method into scope as well as CurrentCulture. R# could add the static usings automatically. Would also work for HTML: + +``` +var name = "x"; +"Hello, \{HtmlEncode(name)}" +``` + +And we could have a tool (R#) check that all holes in the entire solution are properly encoded/formatted. This way we can force compliance in teams and not have encoding bugs. + +We don't even need a default culture now. Just call ToString and whatever that one picks is used. Or better, use the same rules that string.Format uses (which also results in CurrentCulture). + +--- + +On May 10, 2014 at 2:01 AM @MarcosMeli wrote: + +Great discussion !! + +I vote up for a __new string marker for some kind of Razor string__, for example something like: + +``` +var msg = «Hi @name you have won @creditsWon credits» +``` + +It is clear and backward compatible, don't broke anything, in fact going further you can use a prefix to denote that the __string contains html inside (with @prefix)__ , for example: + +``` +var msg = @«
+ Hi @name you have won @creditsWon credits +
» +``` + +Visual Studio and Resharper can provide __full intelisense, html validation, completion and of course compilation support__ + +That syntax is very simple, reuses the Razor knowledge and support of the tooling chain, and with a new string marker you can use inside " and ' without escape them + +You can use inside __@foreach__ __@if__ and most of the Razor options + +The unique counterpart is that the keyboard don't contain the simbols but I don't see it as stopper because it solve ton of other things :) + +Here is the user voice suggestion + +http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5903196-add-new-string-literal-that-contains-razor-code-f + +__Vote it up if you like the syntax and idea of full Razor string__ + +--- + +On May 12, 2014 at 2:33 AM @PauloMorgado wrote: + +**MarcosMeli wrote:** +> The unique counterpart is that the keyboard don't contain the simbols but I don't see it as stopper because it solve ton of other things :) + +**Your** keyboard doesn't contain the symbols. Mine does. + +But that's one of the reasons I prefer a prefix to the string. + +--- + +On May 12, 2014 at 2:53 PM @supercat wrote: + +**PauloMorgado wrote:** +> **Your** keyboard doesn't contain the symbols. Mine does. + +I wish I had a keyboard layout where all ASCII characters could be typed as single characters without dead keys, but diacritics and other special characters could be typed sensibly without silly alt+Number codes. Know of any? + +--- + +On May 12, 2014 at 5:10 PM @JanKucera wrote: + +**supercat wrote:** +> I wish I had a keyboard layout where all ASCII characters could be typed as single characters without dead keys, but diacritics and other special characters could be typed sensibly without silly alt+Number codes. Know of any? + +Fancy to create it? [http://msdn.microsoft.com/en-us/goglobal/bb964665.aspx](http://msdn.microsoft.com/en-us/goglobal/bb964665.aspx) + +--- + +On May 12, 2014 at 7:27 PM @FrankBakker wrote: + +One scenario I think is important is to load (localized) strings from a resource or database and replace named parameters. Probably with some API like string.Format but taking a dictionary instead of an object[]. So besides binding from a string to identifiers in the current scope of a string constant, the same solution should also be able to bind to some sort of dictionary with a simple syntax. + +I really like the Razor syntax, it is existing technology and already supports full expressions with real simple syntax etc. (I voted!). Would be great if Razor could be used inside C# like C# can be used inside Razor. (Up to your co-worker to find out how many levels deep this will go). Currently Razor makes it hard (if not impossible) to create top-level variables that are not statically typed, which makes it impractical for the scenario I described above. The best way I found was providing a single static property of type dynamic so the template can do something like +"Hello @Fields.FirstName" + +(Seems like I just found a use case for the light-weight dynamics that just got dropped :-( ) + +--- + +On May 12, 2014 at 7:37 PM @Halo_Four wrote: + +Well if the key doesn't exist on your stock US keyboard I don't think that you're likely to see it included. :) + +My other concern here is are you advocating for the integration of the Razor engine within the language? If so to me that would mean that a general-purpose language feature would be based on something designed specifically to work with markup, taking dependencies on various libraries that aren't even stock BCL let alone default for the language. How would this behave for strings that aren't intended to be web views? This would also absolutely require some kind of prefix mechanism as embedding that syntax into a string is completely legit now and we wouldn't want the compiler misinterpreting existing code. Lastly, the scenario in which you would be using this seems to violate the tenets of MVC and how Razor is currently employed by encouraging developers to construct the view (or portions of the view) within the controller. + +If that's not the case and you're just looking for a more featureful syntax for string interpolation that feels like Razor is exactly that it would feel like Razor. It wouldn't fit in with the current string formatting syntax used throughout the BCL, and worse developers might confuse it for the fully functional Razor engine which adds extra logic like HTML encoding and partial templates to the raw values being interpolated into the string. That could result in poorly interpolated partial views that contain accidentally dangerous chunks of HTML or script. That and I'm not sure that looping within an interpolation is really a common use-case. + +And that last point is something that scares me about interpolation in general. Where it seems to most apply breaks some best practice for the use of strings within an application, whether that be mixing of concerns between business logic and the view, embedding non-i18n friendly strings in the view or exception messages or dynamically constructing unsafe SQL. Granted, I understand most applications don't need to worry about i18n and that is an absolutely valid point. + +If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to `String.Format` except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with `String.Format`, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to `string.Format`. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity. + +```cs +string name = "Bill Gates"; +DateTime dob = new DateTime(1955, 10, 28); +string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}."; +``` + +--- + +On May 12, 2014 at 7:58 PM @FrankBakker wrote: + +One common scenario for string interpolation if for generating text in some other language, like SQL, HTML, regex or xPath. This has become a sort of anti-pattern, mainly because developers forget to properly escape what should be literals in the target language, causing things like SQL-injection, cross site scripting or just unexpected behavior if there is a special character in a substituted value. + + It would be nice if the interpolation engine would somehow be configurable with an applicable handler that knows how to escape literals in this specific target environment. Kind of like how Rasor by default will html escape any string before sending it to the output. This handler will get a chance to inspect and or update any value before it is pasted into the target string. + +--- + +On May 12, 2014 at 8:04 PM @MarcosMeli wrote: + +**Halo_Four wrote:** +> Well if the key doesn't exist on your stock US keyboard I don't think that you're likely to see it included. :) + + +Of course I just point out that we need a new string marker that don't broke previous code and don't use " mostly because a ton of html and other strings contains " inside and we must use escape chars that make the final code hard to read, I saw in the Build 2014 a demo of Roslyn with a new string marker with a dual ^ that I can't even found on charmap, so is not so crazy to think in a new marker + + +> My other concern here is are you advocating for the integration of the Razor engine within the language? If so to me that would mean that a general-purpose language feature would be based on something designed specifically to work with markup, taking dependencies on various libraries that aren't even stock BCL let alone default for the language. How would this behave for strings that aren't intended to be web views? This would also absolutely require some kind of prefix mechanism as embedding that syntax into a string is completely legit now and we wouldn't want the compiler misinterpreting existing code. Lastly, the scenario in which you would be using this seems to violate the tenets of MVC and how Razor is currently employed by encouraging developers to construct the view (or portions of the view) within the controller. + + +I just suggest a Razor Syntax with some features but it is a __compile time process__, that Roslyn compiler will convert the interpolation to plain C# code, it must be simple enough. __No dll dependencies at all__, and Razor is not just for markup it can be use in plain interpolation with a great flexibility and simplicity allowing not only point to variables also using @if @foreach and accessing methods and properties in a know way + +> If that's not the case and you're just looking for a more featureful syntax for string interpolation that feels like Razor is exactly that it would feel like Razor. It wouldn't fit in with the current string formatting syntax used throughout the BCL, and worse developers might confuse it for the fully functional Razor engine which adds extra logic like HTML encoding and partial templates to the raw values being interpolated into the string. That could result in poorly interpolated partial views that contain accidentally dangerous chunks of HTML or script. That and I'm not sure that looping within an interpolation is really a common use-case. + +I talk about use simple Razor in first place and later I suggest to use an HTML marker to allow resharper and visual studio to use intellisense inside, if no additional marker a lot of tooling must be disabled + +I don't agree about interpolation as a bad practice because allowing Razor strings is not a bad practice, only if you use it wrong, in fact you can always use plain strings wrong anyway + +Best Regards + + + + +--- + +On May 12, 2014 at 8:08 PM @supercat wrote: + +> If we are to have interpolation, I think it should be prefixed and the holes non-escaped. + + +How about `$"Hello "(name)", you were born on "(dob,"20:d")", which is a "(dob,"dddd")$"`? Have the things being evaluated be outside quotes so that Intellisense and refactoring tools would be able to recognize them, and have the above either expand into a string and `Object[]` if such expansion would be legitimate, or into a call to `String.Format` with an appropriate format string and `Object[]`. That would allow efficient use with methods like `AppendFormat`. + +I don't like to have language features overly-strongly tied to a particular method. Something like `Delegate.BeginInvoke` includes what would be the widely-useful type-safe ability to encapsulate a delegate and its parameters into a single object, but only allows that ability to be used with one particular threadpool. If `BeginInvoke` had instead been replaced with a method `Bind` that returned type `MethodInvoker`, code that wanted to start a thread on the default thread pool could have used `System.Threading.DefaultPool.Start(someDelegate.Bind(3,4);)`--a little more verbose but also more specific than `someDelegate.BeginInvoke(3,4)`, but something like `myControl.BeginInvoke(someMethod.Bind(3,4))` would have been type-safe, unlike `myControl.BeginInvoke(someMethod,3,4)`. + +I see a similar situation applying here with `String.Format`. I can see uses for a language feature which would facilitate preparing a format string and argument list in a fashion that would be suitable for use with `String.Format` (each parenthesized expression would insert a `{0}`, `{1}`, etc. tag within the string, and add a parameter to the array), but I see no reason it should need to directly call that method. + +--- + +On May 12, 2014 at 8:19 PM @Halo_Four wrote: + +**FrankBakker wrote:** +> One common scenario for string interpolation if for generating text in some other language, like SQL, HTML, regex or xPath. This has become a sort of anti-pattern, mainly because developers forget to properly escape what should be literals in the target language, causing things like SQL-injection, cross site scripting or just unexpected behavior if there is a special character in a substituted value. +> +> It would be nice if the interpolation engine would somehow be configurable with an applicable handler that knows how to escape literals in this specific target environment. Kind of like how Rasor by default will html escape any string before sending it to the output. This handler will get a chance to inspect and or update any value before it is pasted into the target string. + +I agree. What concerns me is that this becomes a game of whack-a-mole of the language and tidbits of framework both encouraging the user to adhere to the anti-patterns while attempting to prevent them from getting burned. History shows that it's only a matter of time. And PHP is the _last_ language that C# should ever be attempting to emulate. + +--- + +On May 12, 2014 at 8:52 PM @Halo_Four wrote: + +Oof, someone needs to terminate their code block delimiters. :) + +I agree, we'd want the IDE support and Intellisense. I can't imagine that the effort in supporting the syntax would be that different if they went either way. Resharper is going to have to modify their parser in order to pick apart the interpolation syntax. Any project using Roslyn's APIs would get that for free. + +--- + +On May 12, 2014 at 11:14 PM @PauloMorgado wrote: + +**Halo_Four wrote:** +> If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to `String.Format` except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with `String.Format`, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to `string.Format`. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity. + +I agree with you , but I'm afraid the team is very set up on escaped holes instead of prefixed strings. + +> ```cs +> string name = "Bill Gates"; +> DateTime dob = new DateTime(1955, 10, 28); +> string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}."; +> ``` + +The problem with this is, what culture is this using? + +--- + +On May 12, 2014 at 11:18 PM @PauloMorgado wrote: + +**MarcosMeli wrote:** +> I just suggest a Razor Syntax with some features but it is a __compile time process__, that Roslyn compiler will convert the interpolation to plain C# code, it must be simple enough. __No dll dependencies at all__, and Razor is not just for markup it can be use in plain interpolation with a great flexibility and simplicity allowing not only point to variables also using @if @foreach and accessing methods and properties in a know way + +Powershel doesn't need that and has true string interpolation. A lot easier to understand and master than Razor. + + +--- + +On May 13, 2014 at 12:04 AM @Halo_Four wrote: + +**MarcosMeli wrote:** +> I don't agree about interpolation as a bad practice because allowing Razor strings is not a bad practice, only if you use it wrong, in fact you can always use plain strings wrong anyway + +The normal use of Razor is specifically within the view of an MVC application, which is the proper place for it. While you can argue that at that point it is more or less like string interpolation on steroids that is way oversimplifying the purpose. If you start to move Razor into strings within the MVC controller I would argue that you're starting to mix your concerns and that is inappropriate. + +I'm actually finding it very difficult to think of a valid use of interpolation that doesn't mix concerns (view generation in business logic), imply some form of special encoding (SQL, HTML, URI) or that doesn't have some _technically_ more appropriate solution (exception messages, snippets of view text). + +--- + +On May 13, 2014 at 12:16 AM @Halo_Four wrote: + +**PauloMorgado wrote:** +> **Halo_Four wrote:** +> > If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to `String.Format` except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with `String.Format`, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to `string.Format`. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity. +> +> I agree with you , but I'm afraid the team is very set up on escaped holes instead of prefixed strings. +> + +Yeah, and that's fine. It's probably more C#ish than a prefix, particularly `$`. Not my preference, but it's not like this is a democracy. + +> > ```cs +> > string name = "Bill Gates"; +> > DateTime dob = new DateTime(1955, 10, 28); +> > string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}."; +> > ``` +> +> The problem with this is, what culture is this using? + +None. It would translate literally: + +```cs +String.Format("Hello {0}, you were born on {1,20:d}, which was a {1:dddd}.", name, dob); +``` + +Internally that calls `StringBuilder.AppendFormat` without an `IFormatProvider` which basically leaves it up to the arguments themselves, although it seems that the normal behavior is to then use the current culture of the current thread. + +As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes `CultureInfo.InvariantCulture` instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns. + + +--- + +On May 13, 2014 at 1:01 AM @PauloMorgado wrote: + +**Halo_Four wrote:** +> As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes `CultureInfo.InvariantCulture` instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns. + +That was your exemple. And, for me, the output was: +> Hello Paulo, you were born on 1967-07-18, which was a terça-feira. + + +--- + +On May 13, 2014 at 1:15 AM @Halo_Four wrote: + +**PauloMorgado wrote:** +> **Halo_Four wrote:** +> > As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes `CultureInfo.InvariantCulture` instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns. +> +> That was your exemple. And, for me, the output was: +> > Hello Paulo, you were born on 1967-07-18, which was a terça-feira. + +Yep, which is why I'm making the argument against mixing i18n and interpolation. :) + +For those business line apps for which there is no hope or care of i18n then the hardcoded strings and current thread culture would make sense, moreso than invariant culture would. Then you can hardcode your Portuguese strings from hoje to terça-feira. :) + +--- + +On May 19, 2014 at 11:16 AM @AdamSpeight2008 wrote: + + +Will this mean that the string literal is parsed and and available for analysis via Roslyn api? ie + +``` +StringFormatArg::= "{" Index ("," alignment)? (":" format)? “}“ +``` + +Whilst correctly identifying, escaped opening and closed braces? + +This would mean that it isn't just a string literal, it content has a contextual meaning to the surround expression. + +Which could be a powerful feature to have at compile-time. Check that args in the string are they valid? Within range? Is there enough args suppliied along with the formatstring? +String interpolation with named indices,, would just be an addition indexpart parser. +You could go to the exreme and implemement a parse that does arbitrary expressions. Like Nemerle + + + +Then you could classify them as a subclass. + +``` +FormatStringLiteral <: StringLiteral +``` + +Also attribute possibke consumers with additional metadata that the string should be formatstring + +--- + +On May 21, 2014 at 8:37 AM @kfarmer wrote: + +My vote is against interpolation: it ties the string to the details of the variable names, which is almost always the wrong thing to do from a localizability perspective. + +Likewise embedded expressions. + +However, the Python option is friendly to localizable resources, and avoids positional argument hell. Just create a new format method that takes a dictionary to apply to a string that uses named arguments. + +--- + +On May 21, 2014 at 9:10 AM @tocsoft wrote: + +my opinion on string interpolation; + +Create a new struct of with a type name of something line ```InterplotatedString``` which looks like; +```` +public struct InterpolatedString +{ + private string format; + private object[] args; + + public InterpolatedString(string format, params object[] args) + { + this.format = format; + this.args = args; + } + + public string Format + { + get { return format; } + } + + public object[] Args + { + get { return args; } + } + + public override string ToString() + { + return string.Format(format, args); + } + + public string ToString(IFormatProvider provider) + { + return string.Format(provider, format, args); + } + + public static implicit operator string (InterpolatedString interpolatedString) + { + return interpolatedString.ToString(); + } +} +```` +then when using string interpolation instead of directly mapping to a call to string.Format then you instead just wrap to a new instance of ```InterpolatedString``` +this would give us the the benefit of providing programmatic access to the values for those situation where an api doesn't internally use string.format or does some other manipulation first while also having the implicit cast allow other developers to use it as a native string without thought. Additionally due to the overload of ```ToString()``` it would also support passing a format provider for culture support (if required). + +So an end to end translation could be something like this +``` +var t = $"Joe Blogs was born on { joe.Dob : dd-mmm-yy } this was { DateTime.Now.Subtract(joe.Dob).TotalDays } days ago"; +``` +with ``joe`` being a simple POCO with a ```Dob``` property exposing a ```DateTime``` +this would translate to +``` +var t = new InterpolatedString("Joe Blogs was born on {0:dd-mmm-yy} this was {1} days ago", joe.Dob, (DateTime.Now.Subtract(joe.Dob).TotalDays)); +``` + +--- + +On May 21, 2014 at 9:57 AM @PauloMorgado wrote: + +**kfarmer wrote:** +> My vote is against interpolation: it ties the string to the details of the variable names, which is almost always the wrong thing to do from a localizability perspective. + +It's already tied through calls to `string.Concatenate` or `string.Format`. The compiler is just not helping you on that now. + +> Likewise embedded expressions. + +The same. + +> However, the Python option is friendly to localizable resources, and avoids positional argument hell. Just create a new format method that takes a dictionary to apply to a string that uses named arguments. + +Creating a dictionary would involve unnecessary heap allocations. + +--- + +On May 21, 2014 at 10:36 AM @AdamSpeight2008 wrote: + +Have at look at the project [String Format Diagnostic](https://stringformatdiagnostic.codeplex.com/) + +Which provides validation of the text formatstring pre compilation, via roslyn code diagnostic. + +I could imagine a fork that modifies the part of the parser that gets the arg indexbeibg replaced the identify identifiers. +Then check that those are accessible. + +--- + +On Jun 3, 2014 at 12:18 AM @MV10 wrote: + +I don't personally see the need for this over String.Format, and I strongly agree with kfarmer's point about mixing code elements into string literals, but what's really bugging me is calling it "interpolation." It's probably minor compared to the go/no-go question, but I think it's a misuse of the word. It certainly doesn't match the math/engineering uses I'm aware of (simply put, deriving intermediate data points from a known range). + + + +--- + +On Jun 3, 2014 at 7:46 PM @Halo_Four wrote: + +**MV10 wrote:** +> I don't personally see the need for this over String.Format, and I strongly agree with kfarmer's point about mixing code elements into string literals, but what's really bugging me is calling it "interpolation." It's probably minor compared to the go/no-go question, but I think it's a misuse of the word. It certainly doesn't match the math/engineering uses I'm aware of (simply put, deriving intermediate data points from a known range). + +Can't really say when in the history of programming languages that the term "interpolation" took on the meaning of embedding variables in strings but it's certainly not new to C#. If Microsoft went with any other name it would probably be more confusing to the community as a whole. + +Have you looked at the recent announcement of Apple's Swift language? It has interpolation as a feature with the same name and a very similar syntax to the original proposal in Roslyn: `let s = 'Hello \(name)'` + +--- + +On Jun 3, 2014 at 7:49 PM @MV10 wrote: + +Huh. I've been programming more than 30 years and somehow missed that usage completely. Thanks. + +--- + +On Jun 4, 2014 at 1:27 PM @Vincent3000 wrote: + +Hey, my "slow mo friends", STILL DISCUSSING FEATURE???? Look around, VS 2014 CTP is out! _WITHOUT_ interpolation string! WTF is going on, dudes? Was it so hard to implement interpolating strings SOMEHOW? Who care so much how it will look - $name, \{name} or wh@teversh*t{name} - IT IS NOT DONE. + +Thanks everybody for wasting time! Thanks for all your useless comments, esp. "xor88"! Now we nave NOTHING. + +What else you wanna discuss? May be china characters? Or angle of "angle brackets"? C'mon, don't feel shy - put more rubbish in discussion, it's so productive to listen your comparison of GODDAMN SIMPLE INTERPOLATION with your vision of string.Format! + +I need just one answer: who is responsible for "interpolating strings" and why that @%#$@ still didn't do it? + +--- + +On Jun 4, 2014 at 1:40 PM @xor88 wrote: + +I vote for Vincent3000 to be put in charge of the C# language. Finally someone who gets it. Just put all the useful stuff in there already! :) + +--- + +On Jun 4, 2014 at 3:18 PM @lwischik wrote: + +**xor88 wrote:** +> I vote for Vincent3000 to be put in charge of the C# language. Finally someone who gets it. Just put all the useful stuff in there already! :) + +Seconded. We appreciate the offer, Vincent3000. Code up the change, submit a pull request, and we'll review it! :) + +But seriously, Vincent, the discussion here has not played ANY role in delaying the feature. (On the contrary, I suspect we the language team would have simply dropped the feature already without the evidence of demand for it, and without evidence that the community is happy with the design). + +As for the implementation? You can see there's a long list of proposed language features, and we're now working through them gradually, making sure that we get each one in a state we're proud of before moving on to the next. + + +--- + +On Jun 4, 2014 at 8:28 PM @AdamSpeight2008 wrote: + +Similar in concept to __tocsoft__'s but with tweaks +What about these basic class layouts for an implementation. +``` +Class InterpolatedString + Inherits StringLiteral '<-- It's still a string literal + .Args : List + +Class Arg + .Span : TextSpan + .IndexArg : ArgIndexBase + .Alignment : AlignmentPart + .Format : FormatPart + +Base Arg_Base + .Span : TextSpan +Base ArgIndexBase + Inherit Arg_Base + +Class ArgIndex_Indexed + Inherits ArgIndexBase + .Index : Integer + +Class ArgIndex_Identifier + Inherits ArgIndexBase + .Identifer : Identifer + +Class AlignmentPart + Inherit Arg_Base + .Alignment : Integer + +Class FormatPart + Inherit Arg_Base + .Format : String +``` +Which allows the compiler / coder additional access to the content of the interpolated string. +For example renaming of the variable from _joe_ to _alice_ would auto-magically rename the content of the interpolation string. since the arg is accessible and contextually meaningful to the compiler. As well as other code diagnostics. + +The Editor could that apply additional formatting to the text. +Eg + var t = $"__Joe Blogs was born on__ { joe.Dob : _dd-mmm-yy_ } __this was__ { Days } __days ago__"; + +If you want to extend to include expressions inside of the 'holes' + +``` +Class ArgIndex_Expression + Inherits ArgIndexBase + .Expression : Expr +``` + +--- + +On Jun 5, 2014 at 1:25 PM @Vincent3000 wrote: + +**lwischik wrote:** +> We appreciate the offer, Vincent3000. Code up the change, submit a pull request, and we'll review it! :) + +Hey, don't you think I didn't tried?? I did! I was so tired waiting for so simple thing that started feature myself. I implemented "interpolating strings" as string with "christmas tree" quotes («») till stage "source is parsed by compiler and some internal exception happen". Unfortunately I have no access to documentation (if it exists at all) and couldn't "push thru" my "new type string" to AST. But after some discussion I realised that even existing strings can easy adopt new "dollar syntax": +var s = "My name is $name" - this is more than enough for all our needs! No function calls, expressions, etc. Just a simple variable or field/property. Period. + +From my view, better to have AT LEAST SOMETHING than nothing at all! Look at D language - they try, fall, stand up and try again! And their features probed in real life, after what feature grows according to real needs, not according to "vision of C# team". Feel the difference? That's why I asked: "make it any simplest way, doesn't matter" - because (like "conditional access operator") we always can EXCLUDE feature from release! But now we wanna test - how life is good with interpolating strings? + +> ...we... dropped the feature already without the evidence of demand for it + +There IS demand, otherwise we didn't discuss it here, right? :) Just think why it's implemented in Perl, Ruby (may be even Python): because IT IS HANDY! And less error prone. Who wants to deal with all that string.Format positions counting, when we can just write $var? + +> As for the implementation? You can see there's a long list of proposed language features, and we're now working through them gradually, making sure that we get each one in a state we're proud of before moving on to the next. + +I agree, there is A LOT of nice things we would like to have in C#, but don't you have some "professional intuition" related things? For me "static using" is just a "mess producer": when you write method, all functions you call (without object".") is not a "local methods" anymore - it can be any method, imported from 2-5 another classes - GODDAMN, WHY??? Don't you see it's stupid? Or "primary constructor" - same thing, where I'l never waste my time. That's why you have limited time - because wasting it on definitely wrong ideas. And believe me - I could be happy to be in "C# team" to push things forward! _GOOD_ things forward. :)) + +Back to "int.strings": I wish to have it as simple as this: var s = "My name is $name"; Period. No bells and whistles, no coffee maker, no nose trimmer, just "take variable/field/property value and insert into string". Sure any guy in "C# team" can do it in 4 hours, but hell... things move so slo-o-o-ow! + +--- + +On Jun 5, 2014 at 3:30 PM @Halo_Four wrote: + +**Vincent3000 wrote:** +> +> Back to "int.strings": I wish to have it as simple as this: var s = "My name is $name"; Period. No bells and whistles, no coffee maker, no nose trimmer, just "take variable/field/property value and insert into string". + +And you break a ton of existing code as a result. Every tiny change has consequences and anything that makes it into release becomes a permanent part of the language. I applaud the C# team for taking their time and thinking it through while keeping an eye on the next release + + +--- + +On Jun 6, 2014 at 12:37 AM @PauloMorgado wrote: + +@Vincent3000, we couldn't be further apart on this. So far apart that I (that am know to have outbursts like yours but tend not to in such a public forum) will try to give you a polite answer. + +> Just think why it's implemented in Perl, Ruby (may be even Python) + +In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages. + +So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups. + +I can see so many ways that this feature can go wrong that I'm glad it's not in the CTP. But then again I'm not as brilliant smart and know it all like you. + +Please, keep telling us what to think and what to do. I'd hate to think that I'm thinking or doing something stupid (like writing this reply). + +--- + +On Jun 6, 2014 at 1:04 PM @eldritchconundrum wrote: + +**PauloMorgado wrote:** +> In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages. +> +> So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups. + +You seem to be implying that scripting languages are not serious, professional languages. That's wrong. +To me, being a "scripting language" simply means having the quality of being good for interactive use (things like dynamic typing and interpreted/compiled are irrelevant). +Definitely not C#'s strong point right now, but improvements can be gradual. + + +--- + +On Jun 6, 2014 at 7:21 PM @PauloMorgado wrote: + +**eldritchconundrum wrote:** +> **PauloMorgado wrote:** +> > In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages. +> > +> > So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups. +> +> You seem to be implying that scripting languages are not serious, professional languages. That's wrong. +> To me, being a "scripting language" simply means having the quality of being good for interactive use (things like dynamic typing and interpreted/compiled are irrelevant). +> Definitely not C#'s strong point right now, but improvements can be gradual. + +Although I intended to seem like I was diminishing scripting languages, I did not intend to effectively do so. + +These days I find myself doing a lot of things in Powershell that, in the past, I would have built my own tools. + +Extreme careful must be put in changes for them to become improvements. + +--- + +On Jul 18, 2014 at 1:10 AM @Vincent3000 wrote: + +**PauloMorgado wrote:** +> > Just think why it's implemented in Perl, Ruby (may be even Python) +> In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages. + +By your ridiculous logic if cart (with horse) has round wheels, diesel cars should have square wheels (because round wheels is so "horsy"!). +Man, leave programming, because you have no logic at all. +I discuss interpolating string HERE because I need it in C#! Why the hell you advice me scripting?? +And again I see no any progress: people is busy with ridiculous "primary constructor" (which will be used in 0.000001% cases) and do nothing for interpolating strings - what's going on, dudes? +One mistake you do (thinking about yourself as "good" architector) is that every feature you wanna implement should be "The Earth turning tool". Relax, guys, get back to the office! You're not Einstein and we ask you not the space shuttle! Just implement it as simple as possible, ONLY PRACTICE (not your "thinking") can show how you can/could do it. Without real usage you definitely will make mistake in designing feature - because you're just humans and only _some_of_you_ have more-less good capabilities to do "designing" job. Trust community, leave ambitions. :) + +--- + +On Jul 18, 2014 at 7:50 AM @EyalShilony wrote: + +**Vincent3000 wrote:** +> One mistake you do (thinking about yourself as "good" architector) is that every feature you wanna implement should be "The Earth turning tool". Relax, guys, get back to the office! You're not Einstein and we ask you not the space shuttle! Just implement it as simple as possible, ONLY PRACTICE (not your "thinking") can show how you can/could do it. Without real usage you definitely will make mistake in designing feature - because you're just humans and only _some_of_you_ have more-less good capabilities to do "designing" job. Trust community, leave ambitions. :) + +The way I see it is you want them to implement this because you subjectively prioritized it as the number one feature that everyone needs but in fact, while I want it as much as you do I think that you can speak for yourself and not in the name of everyone else, not to mention, that I think you can tone down the language and behave more professionally because you are a professional, right? + +Yes Vincent, it definitely shows in your posts how much you know about design, we have a process called design because we are humans and because we do make mistakes, otherwise, we wouldn't have it at all and every decision was right on the spot. + +I'd say listen to the community but don't trust their judgment, that's a decision too and it's probably the correct bet, do the math. ;) + + +--- + +On Jul 18, 2014 at 1:43 PM @mirhagk wrote: + +I think the key thing is that the first iteration of it must be right, or near enough right. Even though here should be considered experimental, there will always be people that think that's not true, and will expect features not to be dropped. That unfortunately means that we need to discuss what the best option is without having lots of real world data. + +The beautiful thing is that Roslyn is 100% open source, and the source is pretty well architected at that. If you desperately want a feature, and think you're better at doing stuff than the maintainers of the project, you can go ahead and fork it yourself. + +--- + +On Jul 18, 2014 at 3:48 PM @AdamSpeight2008 wrote: + +Why don't you implement the feature yourself? If the implementation is good enough maybe it'll get pulled into the C# compiler official source code. + +--- + +On Jul 23, 2014 at 8:09 PM @Yota wrote: + +OK, so um, I think we jumped off the tracks somewhere. + +My vote would be with the `$"2 + 2 = {2 + 2}"` syntax, allowing arbitrary code in the hole. The only big concern I have with it is the culture. Part of me says it should simply always be InvariantCulture, since any localized strings will probably be stored in resource files, which won't be able to use this syntax. However that would be a pain for the naive people like me who only target a single culture. I don't think we would ever really need to use a culture other than Invariant and the current thread culture, so why not have one more type of string literal to make the distinction? `$"..."` for InvariantCulture, and `$$"..."` or `#"..."` for the current culture. (Or vice versa, actually.) On the edge case where you need to use a different culture, fall back to Format(), or temporarily change the current culture. + +I wonder how VS would colorize this... + +--- + +On Jul 24, 2014 at 12:52 AM @AdamSpeight2008 wrote: + +@Yota Can I also put Interpoled String inside of the arbitrary code hole? also with arbitrary code? + +--- + +On Jul 24, 2014 at 1:29 AM @Yota wrote: + +**AdamSpeight2008 wrote:** +> @Yota Can I also put Interpoled String inside of the arbitrary code hole? also with arbitrary code? + +You would have to be able to since such a string counts as arbitrary code, which is what makes me wonder about the colorization. Perhaps slightly darkening the text or background for each level of depth. + +--- + +On Jul 24, 2014 at 2:04 PM @mirhagk wrote: + +@Yota it looks like they've already nailed down the design for now, although it can of course be extended in the future. + + +For now they are only allowing simple variables inside, no expressions. It'll basically just be syntatic sugar for String.Format. In the future they want to possibly allow: + +``` +LOC$"Hello, {name}, you have {amount} donut\{s\} left." +SQL$"…" +URI$"…" +``` + +Which would pass it to a function that could do escaping etc. I'll be looking forward to this. It could also handle culture and just assume the same default as the rest of .NET (current culture). + +--- + +On Jul 26, 2014 at 3:41 AM @AaronLS wrote: + +I"d like to make some points in the context of a common use I see of string.Format, a situation where the developer doesn't need its formatting capability, but simply to make the string more readable than concatenation: + +``` +string.Format("My name is {0} and I am this many {1}", firstName, age); //not using any formatting, but much more readable +``` + +As mentioned this becomes cumbesome to maintain with large numbers of arguments. Interpolation solves this problem, however, it introduces the requirements that all arguments are variables, unless you allow complex expressions between "{string.Join(',', list)}" which I do __not__ suggest as you'd be writing C# while inside string quotes, thus making it difficult for the IDE to implement any kind of completion/intellisense while writing this expression inside the quotes. But getting to my intended point, if you want to eliminate the dependency on declaring variables then adopt a syntax that emulates named parameters. Imagine if you could do something like this with string.Format: +``` +string.Format( "My name is {name} and I am this many {age}", + name: person.FirstName + " " + person.LastName, + age: person.YearsOld ) +``` +I"m not proposing this as the syntax, but just to exemplify the key features here I'd like to see in the syntax 1) the mapping of a {fieldname} to another syntax element ```fieldname: someExpression``` which seperates the expression from the string itself giving it only a name, and 2) also ensures no local variable is needed to bind to the fieldname. + +This gives you the best of both worlds, with the exception of being a bit verbose. In this case, __the verbosity is kept outside of the string itself__, so __you still get great readability__ and arguably __better arguability than embedding expressions__ since all {variable} instances are kept to a single identifier. + +__If you don't allow an easy way to pull expression OUT of the string but also without forcing use of a local variable, then people will have a tendancy to put expressions inside the string, which will gradaully lead you back to the same issue with string concatenation where your string is constantly interupted by large expressions.__ + +Of course some will argue that we can't force people to write readable code, but we can encourage a pit-of-quality and help lead them to it and certainly make it easier for those who want to do so. + +Another reason to provide a way to pass expressions out of channel and NOT require local vars is for linq projections. + +``` +//convert all failed people found into messages +db.People.Where(p=> p.IsFailed) +.Select( p => new { + Message = string.Interpolate( "{name}'s account is in a failed state due to error: {errorMessage}", name: p.First + " " p.Last, errorMessage: p.Error, + ... +}).ToList(); +``` + +Regarding the issue "it's hard to draw a clear compiler-enforced line between "things that are okay in holes" and "things that aren't"", if you only allow single identifiers like varaiables/param names, then in the above even something as simple as p.Error would not be possible as I'd have to stage p.Error into some variable to accomplish this. + +I hope you can see the value of this proposed concept of allow expressions but forcing them to be seperated by some sort of named param (I say named param, but hopefully one reallizes I don't mean that in the strict sense of leveraging the existing C# named param feature). This mapping will allow both easily readable self documenting strings, reordering of params since they are named, and allow one to easily see where each param comes from without having to count the params as you would with {0}. + +How nice it would be if instead of +``` +string url = string.Format("{0}://{1}.{2}/{3}", someScheme, someSubdomain, Request.Current.Host, relativeUrl); +``` + +I could make the string itself more readable: +``` +string url = string.Format("{scheme}://{sub}.{host}/{relUrl}", scheme: someScheme, sub:someSubdomain, host:Request.Current.Host, relUrl, relativeUrl); +``` + +Cheers + +--- + +On Jul 27, 2014 at 11:46 PM @PauloMorgado wrote: + +Aaron, + +Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0. + +Using the syntax you propose, the compiler would build an IDictionary. For any method, not string.Format in particular. + +One of the overloads of string.Fromat would take an IDictionary and use it like you propose. + +--- + +On Jul 27, 2014 at 11:55 PM @PauloMorgado wrote: + +**PauloMorgado wrote:** +> Aaron, +> +> Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0. +> +> Using the syntax you propose, the compiler would build an IDictionary. For any method, not string.Format in particular. +> +> One of the overloads of string.Fromat would take an IDictionary and use it like you propose. + + + +--- + +On Jul 28, 2014 at 9:06 AM @tom103 wrote: + +**PauloMorgado wrote:** +> Aaron, +> +> Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0. +> +> Using the syntax you propose, the compiler would build an IDictionary. For any method, not string.Format in particular. +> +> One of the overloads of string.Fromat would take an IDictionary and use it like you propose. + + +IMO that would be *much* more useful than `params IEnumerable`... + +--- + +On Jul 28, 2014 at 10:30 AM @PauloMorgado wrote: + +**tom103 wrote:** +> IMO that would be *much* more useful than `params IEnumerable`... + +IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query. + +--- + +On Jul 28, 2014 at 10:37 AM @tom103 wrote: + +**PauloMorgado wrote:** +> IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query. + +It's nice, but it just saves you a call to `ToArray`... (OK, calling `ToArray` also causes immediate execution of the query, but I don't think many methods rely on the list of arguments being lazily evaluated). + +--- + +On Jul 28, 2014 at 4:19 PM @PauloMorgado wrote: + +**tom103 wrote:** +> **PauloMorgado wrote:** +> > IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query. +> +> It's nice, but it just saves you a call to `ToArray`... (OK, calling `ToArray` also causes immediate execution of the query, but I don't think many methods rely on the list of arguments being lazily evaluated). + +There must have been enough requests for it. I'm sure it wasn't done just for the fun of it. Right, fellow developers? + +--- + +On Jul 30, 2014 at 9:40 AM @richardtallent wrote: + +It seems like the *real* problem we're trying to solve *isn't* trying to completely replace Composite Format Strings -- developers can always write their own code to parse a string and replace variables if they want really fancy semantics in the holes. + +The real problem is that you have to mentally jump back and forth between the holes in the format string and the position of the corresponding other `String.Format` arguments. + +I don't think a new language feature to embed code inside literal strings is the way to go. Use of literal strings should be an edge case, and giving literal strings a strong advantage for "interpolation" would encourage "magic string" anti-patterns. + +Here's the sort of sugary-sweet call I think would be useful, and would still be able to take a variable template: + +``` C# +String.Format( + "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", + name: user.name, + birthdate: user.birthdate +); +``` + +To make this happen, the C# language could be extended to support a more evolved version of the `params` keyword that allows the caller to pass a dynamic number of *named* parameters of a given type: + +``` C# +String.Format(string format, namedparams object[] args); +``` + +Inside the method, this would be exposed as, say, a `List>`. + +This could be used in situations other than string formatting. It would be a good way to evolve the (relatively recent) named argument capability in C#, and it would be relatively easy to extend to VB as well. + +Now, if you want to have some really fancy custom logic in how the holes are interpreted, another overload of `String.Format` would be useful, and wouldn't require any change to the language: + +``` C# +public interface IFormatItemResolver { + public object GetValue(string formatItemName); +} + +String.Format(string format, IFormatItemResolver) +``` + +This would be analogous to the `RegEx.Replace` overloads that take a `MatchEvaluator` argument. I don't think this solves the simpler case that named parameter lists would, but it would give people more options for dynamic replacement without reinventing the wheels inside of `String.Format`. + +--- + +On Jul 30, 2014 at 10:58 AM @tom103 wrote: + +**richardtallent wrote:** +> ``` C# +> String.Format(string format, namedparams object[] args); +> ``` +> +> Inside the method, this would be exposed as, say, a `List>`. + +It would be very confusing to have a different type from the caller perspective and inside the method... And anyway I don't see how that would work at the CLR level. Why not just declare args as a dictionary? + +```C# +public static string Format(string format, params IDictionary args); +``` + +--- + +On Jul 30, 2014 at 1:06 PM @eldritchconundrum wrote: + +> ```C# +> public static string Format(string format, params IDictionary args); +> ``` + +A beautiful syntax for an excellent idea. This can be very useful for many functions, way beyond string interpolation. + + +--- + +On Jul 30, 2014 at 1:30 PM @Olmo wrote: + +You don't really need params IDictionary, you can use anonymous classes like here: http://blog.dotnetwiki.org/2009/01/16/NamedFormatsPexTestimonium.aspx + +Anyway, one small problem with this approach is that Translation software will confuse the variable identifiers and translate them too: + +Bing translator EN -> ES + +Hi {0} -> Hola {0} +Hi {name} -> Hola {nombre} // bang! + +So If this gets implemented somebody should tell, at least, Bing Translator team + + +--- + +On Jul 30, 2014 at 4:09 PM @tom103 wrote: + +**Olmo wrote:** +> You don't really need params IDictionary, you can use anonymous classes like here: http://blog.dotnetwiki.org/2009/01/16/NamedFormatsPexTestimonium.aspx + +Or like here: https://github.com/thomaslevesque/NString#stringtemplate +But the trouble with this approach is that it uses reflection, so it's much slower... In my implementation, I maintain a cache of property getters to improve performance, but it's still about 5 times slower than `String.Format`. The dictionary approach wouldn't have this problem. + + +> Anyway, one small problem with this approach is that Translation software will confuse the variable identifiers and translate them too: +> +> Bing translator EN -> ES +> +> Hi {0} -> Hola {0} +> Hi {name} -> Hola {nombre} // bang! +> +> So If this gets implemented somebody should tell, at least, Bing Translator team + +I've been using the `StringTemplate` mentioned above in production apps for almost two years, and so far there hasn't been a single case where the translators tried to translate the identifiers. They seem to understand that the braces mean "do not touch" ;) + +--- + +On Jul 30, 2014 at 4:21 PM @richardtallent wrote: + +**tom103 wrote:** + +> It would be very confusing to have a different type from the caller perspective and inside the method... And anyway I don't see how that would work at the CLR level. Why not just declare args as a dictionary? + +I agree that we shouldn't imply an array (the "[]") and then expose a List -- the danger of typing this at 3:30am I'm afraid. + +I like that you've overloaded `params` with a specific type rather than creating a new keyword. I also like that `IDictionary<>`, like my version, allows other uses beyond `String.Format` that may want a different parameter value type than `object`. + +But I do have some concerns about using `IDictionary<>` this way, and think a new type is called for: + +1. Because of the nature of `params`, the key will always be a string, so we shouldn't require/allow that type to be specified in the method definition. +1. `String.Format` performance is important, and the creation of a concrete dictionary and using it within `String.Format` may not be the best strategy. We should let `String.Format` (and other methods taking in named parameters) decide if it's worth the effort to deal with buckets, hash codes, etc. vs. a simpler approach. +1. Because the compiler is generating a concrete object to pass as the "real" argument, we don't need to use an interface here, we can have a generic type that is fit for purpose. +1. `IDictionary<>` is defined in `System.Collections.Generic`, I'm not so sure Microsoft would be interested in having such a low-level reference to it baked into the language. Same goes for `List<>` and `KeyValuePair<>`. +1. While `String.Format` doesn't, some users of these parameters may expect them to be accessed in the same order as they were provided in the call. A Dictionary won't guarantee that. + +The existing `params` functionality uses arrays, and that would be fine for this purpose as well. The called method can decide if and when to convert the array to another data structure, such as a dictionary, if it will likely improve performance. + +But if we pass an array, we need a structure for our keys and values. Simple enough: + +``` C# +struct NamedParam { + public string Key; + public T Value; +} +``` + +Now our method definition looks like this: +``` C# +public static string Format(string format, params NamedParam[] args); +``` + +The only down side here is that the compiler would need to resolve that `NamedParam` is a special type and that the arguments will look different than normal `params`. A new keyword like `namedparams` might be needed to resolve this. + +@eldritchconundrum, I agree that named variable parameters could be incredibly useful for other purposes. :) + +@Olmo, translation is a good point. Anonymous types would work if you could pass them. But even if you could, you'd have to use reflection to get to the keys, which would be messy. + +--- + +On Jul 30, 2014 at 5:01 PM @tom103 wrote: + +**richardtallent wrote:** +> 1. Because of the nature of `params`, the key will always be a string, so we shouldn't require/allow that type to be specified in the method definition. + +Well, you can always declare an interface like that: + +``` +public interface INamedParameters : IReadOnlyDictionary { } +``` +(making it a read-only dictionary seems reasonable since it wouldn't make sense for the callee to modify the values) + + +> 2. `String.Format` performance is important, and the creation of a concrete dictionary and using it within `String.Format` may not be the best strategy. We should let `String.Format` (and other methods taking in named parameters) decide if it's worth the effort to deal with buckets, hash codes, etc. vs. a simpler approach. + +Hard to be sure without benchmarking, but I agree that, since there will typically be few arguments, the dictionary created by the compiler should probably be implemented as an array or a linked list, rather than a hashtable. Anyway, that's an implementation detail that will have to be worked out by the team. + +> 3. Because the compiler is generating a concrete object to pass as the "real" argument, we don't need to use an interface here, we can have a generic type that is fit for purpose. + +I think it's cleaner to use an interface; I don't see any good reason to use a concrete type instead + +> 4. `IDictionary<>` is defined in `System.Collections.Generic`, I'm not so sure Microsoft would be interested in having such a low-level reference to it baked into the language. Same goes for `List<>` and `KeyValuePair<>`. + +I don't think it matters; the planned features for C# 6 already include `params IEnumerable`, and `IEnumerable` is also defined in `System.Collections.Generic`. + +> 5. While `String.Format` doesn't, some users of these parameters may expect them to be accessed in the same order as they were provided in the call. A Dictionary won't guarantee that. + +I don't understand what you mean; the order would matter if the parameters were evaluated lazily, but that's not the case: they will be evaluated before the method invocation anyway, so why would it matter that they're accessed in a different order? And anyway, `Dictionary` doesn't maintain the order (because it's implemented as a hashtable), but other implementations could. + +> +> The existing `params` functionality uses arrays, and that would be fine for this purpose as well. The called method can decide if and when to convert the array to another data structure, such as a dictionary, if it will likely improve performance. +> +> But if we pass an array, we need a structure for our keys and values. Simple enough: +> +> ``` C# +> struct NamedParam { +> public string Key; +> public T Value; +> } +> ``` +> +> Now our method definition looks like this: +> ``` C# +> public static string Format(string format, params NamedParam[] args); +> ``` +> +> The only down side here is that the compiler would need to resolve that `NamedParam` is a special type and that the arguments will look different than normal `params`. A new keyword like `namedparams` might be needed to resolve this. + +An array doesn't let you easily fetch the value associated with a key; that would be quite inconvenient... + +> Anonymous types would work if you could pass them + +But you *can* pass them; an instance of an anonymous type is just like any other object. The only issue is that you can't access the definition of the type, so you have to declare the parameter as `object` or make the method generic; any of these would work : + +``` +public static string Format(string format, object parameters); +public static string Format(string format, T parameters); +``` + + +--- + +On Jul 30, 2014 at 5:19 PM @AaronLS wrote: + +**richardtallent wrote:** + +> Here's the sort of sugary-sweet call I think would be useful, and would still be able to take a variable template: +> +> ``` C# +> String.Format( +> "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", +> name: user.name, +> birthdate: user.birthdate +> ); +> ``` + +This is pretty much exactly what I suggested, I think? That example pretty much covers what I feel would be really great. The string is compact and readable, all {} holes are self documenting, any expressions are abstracted out to the named params to ensure they don't break up the readability of the string. It also forces you to give an expression some arbitrary name, which improves the self documenting aspect of the code since sometimes people in a hurry embed expressions in string concatenation which are not completely self explanatory. + +__I could actually implement this very easily using a Roslyn code transformation extension.__ You take calls like this: + + +``` +String.Interpolate( + "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", + birthdate: user.birthdate + name: user.name, +); +``` + +Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index: + +``` + String.Format( "Your name is {0} and your birthdate is {1:yyyy-mm-dd}", + new object[] { user.name, user.birthdate }; + +``` + +So it is purely syntactic sugar. __No CLR change needed at all!__ + +Note I am not necessarily proposing String.Interpolate( as the syntax for this feature. It would certainly be weird for the compiler to do special handling for a specific method call of a certain Name. Just presented it that way because it's easiest to show as a proof-of-concept that it _could_ be implemented without a CLR change. + +--- + +On Jul 30, 2014 at 6:34 PM @supercat wrote: + +**tom103 wrote:** +> > But if we pass an array, we need a structure for our keys and values. Simple enough: + +Having the structure include `Type` as well could be useful for some situations, such as in logging utilities (where it may be useful to distinguish between e.g. a null string, a null reference to a control, and an `int?` whose `HasValue` is false. My preference would probably be to say that a parenthesized expression that may contain commas will match an overload parameter `ParamInfo params params[]` [moving `params` in the middle would make clear that even if the argument was of type `ParamInfo[]` it should still be wrapped in another `ParamInfo[]` array]. The name associated with each item would be the expression used to produce it *unless* the item started with a name and a colon, in which case the text before the colon would be the name. + +Although retrieving items from arrays is generally less efficient than retrieving items from a `Dictionary`, for typical numbers of arguments it would likely be cheaper to use an array and a linear search than to construct a hash table which would be used once and discarded. Especially given that items would often be used in order, a linear search which always starts immediately following the previous item found and is willing to wrap around once would almost certainly be faster in 99% of applications than would a `Dictionary`. + +--- + +On Jul 30, 2014 at 6:52 PM @AaronLS wrote: + +**supercat wrote:** +> for typical numbers of arguments it would likely be cheaper [...] than to construct a hash table which would be used once and discarded. + +Agreed, for very low number of arguments linear search would _probably_ be faster(benchmarking of course to confirm). This is often true for any algorithm designed to scale well. There are _some_ O(n) algorithms that perform better than O(1) algorithms for a small enough list. This is why many implementations for quick sort will use a different algorithm for handling smaller lists than for larger lists. + +Given the use cases for this feature, params count should be low. If someone is building a really huge string with a very larger number of holes, they 1) Probably shouldn't be inlining that string as a constant 2) use a lightweight templating framework like dotliquid. + +Given that, the performance implications of argument counts should be considered negligible IMHO. __So the design should consider other factors for performance/optimization as well as prioritizing ease-of-use.__ I.e. if the consensus is one design has better ease-of-use, I'd be in favor of that design over one that is simply designed to be more performant for a very large number of params. + +--- + +On Jul 30, 2014 at 10:00 PM @richardtallent wrote: + +**AaronLS wrote:** + +> Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index: + +Good idea for a proof of concept, but unless I'm mistaken, wouldn't that only work for literal strings, since it's happening at compile time? + +--- + +On Jul 30, 2014 at 10:41 PM @richardtallent wrote: + +**supercat wrote:** + +> > > But if we pass an array, we need a structure for our keys and values. Simple enough: +> +> Having the structure include `Type` as well could be useful for some situations + +I'm not following. My example `NamedParam` is generic for the value type, so `String.Format` can use `object` (as it is wont to do) but other uses of the variable named parameters could be of another type. + +> Although retrieving items from arrays is generally less efficient than retrieving items from a `Dictionary`, for typical numbers of arguments it would likely be cheaper to use an array and a linear search + +Yup, I came to the same conclusion. + +A naive O(n) search could be added to the array via an extension method to provide Dictionary-like access: + +``` + public static bool TryGetValue(this NamedParam[] array, string key, out T result) { + if(key!=null && array!=null) { + foreach(var kv in array) { + if(kv.Key==key) { + result = kv.Value; + return true; + } + } + } + result = default(T); + return false; + } +``` + +A more robust approach would be to, say, have an extension method that returns a new "searcher" object that can have its own state so it can implement searches using a hashtable, look-after-last-found, or other algorithm. + +--- + +On Jul 30, 2014 at 10:47 PM @richardtallent wrote: + +Side note -- should we move this to another thread to talk more generally about the idea of variable named parameters? + +--- + +On Jul 31, 2014 at 4:45 AM @AaronLS wrote: + +First, __can I get some concensus from others on one issue.__ It seems most of us currently active in the discussion want some variation of named params to support this. Primarily, __I don't think any of us want to be forced to declare variables for every single hole__, and we also recognize the risks and complexity of allowing expressions inside the string so we are in favor of the {} holes being simple identifiers. This led us to considering some form of named params to support these desires. + +__None of us want it to be like this, correct?__ In terms of being forced to declare variables: + +``` +string name = user.Name; +string birthdate = user.Birthdate; +string interpolated = $"Your name is {name} and born {birthdate}"; +``` + +My fear is this is how they are planning to implement it, based on the OPs post. I see mention of the design having been "nailed down" but I don't see where they publicized their ultimate decision. I am a bit fearful. Once they implement this and release it, I fear what we are proposing is too much of a feature overlap and yet not an interative tweak/improvement of their syntax, such that our approach would never be considered. + +**richardtallent wrote:** +> **AaronLS wrote:** +> +> > Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index: +> +> Good idea for a proof of concept, but unless I'm mistaken, wouldn't that only work for literal strings, since it's happening at compile time? + +Yes that is a limitation, but also means you get some __compile time verification__ that the names in the literal string match up with the named params. All the strings where I've ever used string.Format with "{0}" holes were literals. The parts that were dynamic, were the parts that went into the holes. As OP points out though, there will be some using resources(in 10 years and 4 places, I have yet to work somewhere that was building an internationalized app and so resources were never used). + +If you wanted a feature like this which is evaluated at runtime, you can accomplish that today without a great deal of difficulty(there are already templating libraries that do almost exactly this): + +``` +string templateMaybeFromResource = "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}"; +string rendered = Library.Interpolate("Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", + new Dictionary{ + { "name", user.Name }, + { "birthdate", user.Birthdate }, + }); +``` +or dotliquid style with anonymous type(this requires reflection to work however: +``` +string rendered = Library.Interpolate("Your name is {{name} and your birthdate is {birthdate:yyyy-mm-dd}", + new { + name, user.Name , + birthdate, user.Birthdate + }); +``` +The only substantial thing extra is the ```new Dictionary{```. If we had list initializers for generic types which inferred the type , then that'd cut it down to ``` new Dictionary{```. + +It seems __most of us currently active here are in favor of having very simple named {} holes, with some sort of named list/params of expressions outside of the string__. As opposed to embedding expressions into the string, and also opposed to being required to have variables/params declared that match the named holes(which is what seems to be implied in the OP's post). + +We differ on how we propose we get there, mainly into two big groups: +__Camp A__: Make passing this variable list of named parameters easier. This would be syntactic sugar to lighten the syntax for the list initializer. Within this camp are differing opinions on what the syntax for this should be, and what list/array type should actually be used. + -Pro: Applicable to a wider variety of scenarios beyond string interpolation. + -Con: No guarantee the holes in the string are valid and match the given names at compile time. + +__Camp B__: (not sure anyone else is in this camp besides me) A syntax specific and applicable only to string interpolation, with the string literal evaluated at runtime. + -Pro: Compile time safety against the string literal. + -Pro: Potential for optimizations, inlining references to constants, etc. + -Pro: Could invent a very terse syntax that doesn't even involve a function call ```"Your name is {name} and born {birthdate}"~> new { name: user.Name, birthdate = user.Birthdate }``` + -Con: Works only with string literals. + +They are not necessarily mutually exclusive, you could implement _A_ and also implement compile time verification of the string and potential optimizations. +1) Both _A_ and _B_ provide the same ease of use for string interpolation +2) _A_ is applicable/useful to a much wider set of scenarios +3) _B_ is limited to compile time interpolation + +I am honestly on the fence about it now. __I'd be happy to see either, so long as I don't have to declare local variables, as the OP seems to imply.__ Both _A_ and _B_ are probably negligible difference in terms of effort to support. I could do the same proof-of-concept code transformation to replace named params with a list initializer, mainly because named params are already a syntax roslyn supports. + +_A_ seems a winner because it is applicable to a much wider scenario. However, it doesn't accomplish a great deal more than simply making list initializers for named pairs a little more terse syntax wise IMO. + +My only love for _B_ is my love of the compiler doing anything and everything it can to catch things upfront. + +--- + +On Jul 31, 2014 at 5:38 AM @richardtallent wrote: + +I got to thinking about this tonight again, and I wonder -- would the C# team really be interested in *adding* functionality to `params`, or is it just there for feature parity with C++'s `va_arg`? + +If there is concern adding this feature, an excellent compromise (that would be *incredibly* useful in other situations, even more than variable named parameters) would be **implied collection initialization**. This would just be syntactic sugar for collection initialization where the result is a known type. + +Here would be the new overload in `String`: +``` +public static string Format(string format, Dictionary args); +``` + +And here's how we would call it: +``` +var result = String.Format( + "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", { + { "name": user.name }, + { "birthdate": user.birthdate } +}); +``` + +All we're getting rid of is the extra `new Dictionary`. I realize this adds complexity to choosing the right overload, but this decision would be made by the compiler, not the runtime. + +Going further, it would be even more useful for the compiler to be able to convert an anonymous type *to* a collection initializer on compilation, basically constructing the above (complete with the `new Dictionary`) from a call like this: + +``` +var result = String.Format( + "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", { + name: user.name, + birthdate: user.birthdate +}); +``` +Boom. No more braces around the properties or delimiters around the names, and since the **compiler** is just rewriting the anonymous type as a initialized `Dictionary` instance, there are no changes to the runtime and no new keywords needed. I can think of a thousand uses for this, it would essentially make creating `Dictionary` instances nearly as simple as JavaScript associative arrays. + +And really, there's no reason why this should be tied specifically to System.Collections.Generic.Dictionary. The compiler needs to know which specific class to instantiate and initialize (not just the interface), but the method could specify any class that implements IDictionary with a string key and value type that is compatible with the anonymous type's values. + +``` +public class MyDictionary : IDictionary {} + +MyDictionary colors = { + red: "#FF0000", + blue: "#0000FF", + green: "#00FF00" +}; +``` + +This puts the power in the hands of the `String.Format` implementers to decide what the default passed collection type should be -- they may choose Dictionary, or they may choose something that implements `IDictionary` without the hashtable overhead. (They could also provide an `IDictionary` overload for developers who want to pass in some other means of doing the named lookup). + +Despite the necessary extra brace pair, I think I would love having this even more than variable named parameters, and for `String.Format`, it would be a great way to solve issue at hand. + +--- + +On Jul 31, 2014 at 5:46 AM @ljw1004 wrote: + +Camp A: this is what I had in mind when I wrote the OP and described the "Python" approach. Several people have written in this thread to say they already use this in their production code. + +It would really take off with a convenient syntax for writing dictionary literals. For instance, imagine if `{name:"Jones", age:15}` was a shorthand notation for constructing a Dictionary. Then you could write an extension method that makes this work: + +```cs +"Hello {name} you are {age} years old".Format( {name:customer.Name, age:customer.Age} ); +``` + +Compile-time checking would still be fine. With Roslyn "analyzers", it's easy to write code analyzers, and we're encouraging library-authors to also implement library-specific diagnostics. In this case the library that contains the extension method "String.Format" would also contain a diagnostic that parses the string (if it's a constant string literal) and would provide warnings if the dictionary keys didn't match (if it was a dictionary literal). + + + +Many of the C# language design team folks are out on summer holidays at the moment. The language design meeting won't re-convene for another 3-4 weeks. + + +However, I disagree with the premise this branch of the discussion has taken. The code above just isn't as convenient as string interpolation. The following reads MUCH more easily. + +```cs +$"Hello {customer.Name} you are {customer.Age} years old" +``` + + + +I think it'd be ideal to add both string interpolation and dictionary literals. + + +--- + +On Jul 31, 2014 at 5:56 AM @ljw1004 wrote: + +**richardtallent wrote:** +> And really, there's no reason why this should be tied specifically to System.Collections.Generic.Dictionary. + +You'd certainly want the dictionary literal to a type (presumably either System.Collections.Generic.Dictionary or ImmutableDictionary) if none is provided by the context: + +`var x = { red:"#FF0000", green:"#00FF00"};` + + +Beyond that, you'd certainly want the dictionary literal to work with both Dictionary and ImmutableDictionary at a bare minimum. So yes, as you described, if the context provides a type that's compatible, I agree it should pick that up. + + +--- + +On Jul 31, 2014 at 6:01 AM @richardtallent wrote: + +**AaronLS wrote:** + +> First, __can I get some concensus from others on one issue.__ It seems most of us currently active in the discussion want some variation of named params to support this. + +Yup. + +It appears we were both writing our responses at the same time, since I didn't see yours until I posted. + +I understand the potential up-side in terms of compiler validation when using string literals, but that would also mean that the compiler would have to be hard-wired to look for the holes and corresponding arguments passed to all of the BCL methods that use the .NET Composite String Format syntax (it's not just String.Format, there are a number of others -- see [here](http://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx) for more details). + +That's a ton of compiler work to support only a few methods, and severely limits the usefulness for other places where one might want to use a sweeter way of passing names and values (or, as my last message describes, initializing string-keyed dictionaries, whether for a method call or not). + +I've seen a recent demo for adding a VS plugin to detect and correct various code smells. If this were implemented in a way that follows the "A" camp (generally applicable and can be used on non-literal strings), it may be still possible to add IDE / compiler warnings when you *are* formatting a string literal and the key names don't match the string "holes." + +> I could do the same proof-of-concept code transformation to replace named params with a list initializer, mainly because named params are already a syntax roslyn supports. + +I looked at some sample Roslyn transformation code and ran screaming from the room. Does the method I'm suggesting above (transforming from an anonymous type to an appropriate IDictionary implementation) seem reasonably possible? + +--- + +On Jul 31, 2014 at 6:18 AM @richardtallent wrote: + +**ljw1004 wrote:** +> You'd certainly want the dictionary literal to a type (presumably either System.Collections.Generic.Dictionary or ImmutableDictionary) if none is provided by the context: +> +> `var x = { red:"#FF0000", green:"#00FF00"};` +> +> Beyond that, you'd certainly want the dictionary literal to work with both Dictionary and ImmutableDictionary at a bare minimum. So yes, as you described, if the context provides a type that's compatible, I agree it should pick that up. + +For `var` assignments, I think an anonymous type is still the correct result, even without the "new" keyword. + +What I'm suggesting is just that we "borrow" the syntax of anonymous types to instantiate and initialize specific types that implement `IDictionary` when that type is implied by the context (and the values of the anonymous type's properties can be assigned to T of course). + +Now if we said `IDictionary x = { red:"#FF0000", green:"#00FF00"};`, a `Dictionary` does seem like a reasonable default. Same goes if we're trying to pass it to a method and the "best" overload (i.e., not `object`) is `IDictionary`. + + +--- + +On Jul 31, 2014 at 6:30 AM @AaronLS wrote: + +The first example is definitely nice. + +```$"Hello {customer.Name} you are {customer.Age} years old"``` + +I don't find this terrible, but my reasons for not preferring it: +1) I find my parameters to string.Format often involve expressions, not just identifiers. (and expressions shouldn't be allowed in the string) +2) Anything beyond a single identifier could be an invitation for trouble. Say you save some user's message to a database but using interpolation on it first. Perhaps the literal is "User left note {Message.Note}". That is safe, you decided what the string literal was for interpolation, so you know the only information exposed is Message.Note. __BUT__ later on you retrieve this from DB, and use string interpolation on it again to embed that string into another string. Anytime you have a scenario where something could be interpolated twice(perhaps non obviously), the second interpolation is dangerous. This is because the user could have entered "{Request.Context.SomeSensitiveServerConfig}" which would do nothing on the first interpolation, but the second would then expose the value of that property. (I know some people cache information/configuration in properties for efficiency.) +3) Even with a single identifier, double interpolation might expose a local variable or field int he context of the current class. + +The named params examples make it explicit what you expose to the interpolation. Safe templating engines always require you to opt-in to exposing data to the template. The named params is this opt-in facility. + +--- + +On Jul 31, 2014 at 6:37 AM @ljw1004 wrote: + +**AaronLS wrote:** +> DOUBLE INTERPOLATION +> 2) Anything beyond a single identifier could be an invitation for trouble... Anytime you have a scenario where something could be interpolated twice(perhaps non obviously), the second interpolation is dangerous. This is because the user could have entered "{Request.Context.SomeSensitiveServerConfig}" which would do nothing on the first interpolation, but the second would then expose the value of that property. + +That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code. It only ever looks at the {...} holes in the _literal_. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible. + +Please try writing out your example step by step, showing every statement that gets executed, and we can pinpoint precisely where. + + +I still disagree with your point 1. Consider: + +```cs +var msg = "There are { reds.count + blues.count } balls in the jar"; +var msg = "There are {count} balls in the jar".Format( {count : reds.count + blues.count} ); +``` + +The first one reads more fluently. They're both doing exactly the same thing, but the second one introduces an extra layer of indirection that stops you reading it as easily, and forces you to invent a name "count" for something that you normally don't care to name. + + +--- + +On Jul 31, 2014 at 7:04 AM @richardtallent wrote: + +**ljw1004 wrote:** + +> var msg = "There are { reds.count + blues.count } balls in the jar"; + +I agree that it's fluent, but here are my big concerns: + +* We're getting dangerously close here to re-implementing ASP.NET's `<%=%>` syntax, within a string instead of a page. +* This is "inception code" -- code within a string within code. This means IDE support for things like refactoring, autocompletion, etc. become a lot harder. +* What types of expressions are supported? Can someone, say, put a LINQ statement within the braces? What if someone wants to use a `switch{}` statement or something else that is a valid C# expression but requires the use of its own braces? What if someone tries to embed another string literal inside the expression? What if that string literal contains its own "holes"? +* How are cultural settings applied to the implied ToString() calls that the holes get translated into? +* Having such a deep language feature that *only* work with literal strings not only blocks out a large number of potential uses, it actively *encourages* coding using string literals for all sorts of situations where the string template really shouldn't be embedded in the codebase. For example, I can definitely see novice (or lazy) developers trying to use this to create dynamic SQL, which could be very, very bad. +* This basically creates an *alternative* to String.Format and other CFS-based methods rather than just *fixing* them. + +--- + +On Jul 31, 2014 at 8:02 AM @ljw1004 wrote: + +**richardtallent wrote:** +> * We're getting dangerously close here to re-implementing ASP.NET's `<%=%>` syntax, within a string instead of a page. + +Arguably a good thing! Also re-implementing VB's `<%= %>` syntax as well. + +> * This is "inception code" -- code within a string within code. This means IDE support for things like refactoring, autocompletion, etc. become a lot harder. + +Try using VB's `<%= %>`. The intellisense is fine! + +> * What types of expressions are supported? + +Proposal for C# is to only allow dot-separated identifiers and nothing else, at least as a first step, since this is easy to implement. + +Swift allows any expression so long as it doesn't contain a quote character or a newline. + +VB's `<%= %>` _allows_ arbitrary expressions. + +I think VB has the right idea. It's up to you the developer not to shoot yourself in the foot. Developers can go crazy in a whole bunch of places (e.g. you can pass lambdas inside lambdas inside lambdas as an argument to a method??! you can put await in arbitrary contexts e.g. "f(ref arr[await t])" ??!). I think it always makes for a cleaner language when you make it fully compositional, so you can put anything in, and leave it to developers to keep it sensible. They generally will, as we've seen. + + +> * How are cultural settings applied to the implied ToString() calls that the holes get translated into? + +Proposal is to use the current culture. That's what .ToString() does. That's what String.Format() does if you don't pass extra arguments. + + +> * I can definitely see novice (or lazy) developers trying to use this to create dynamic SQL, which could be very, very bad. + +Proposal is to allow e.g. `Foo$"from {x} in {y} select {z}"` which gets translated into `Foo("from {0} in {1} select {2}", x, y, z)`. This is a direct copy of what Javascript will do to, for precisely the reason you mentioned. The "Foo" can be any method or delegate. It will be able to escape everything as needed. This mechanism can also be used to pick something other than the current culture. + +With this kind of guard, I think it's no longer "very very bad". On the contrary it becomes a clean readable syntax that's also safe. + + +> * This basically creates an *alternative* to String.Format and other CFS-based methods rather than just *fixing* them. + +Rather, proposal is to be syntactic sugar for String.Format; not an alternative. + + + +--- + +On Jul 31, 2014 at 12:51 PM @Halo_Four wrote: + +**ljw1004 wrote:** +> **richardtallent wrote:** +> > * What types of expressions are supported? +> +> Proposal for C# is to only allow dot-separated identifiers and nothing else, at least as a first step, since this is easy to implement. +> +> Swift allows any expression so long as it doesn't contain a quote character or a newline. +> + +The complicating matter here it seems are the formatting expressions used by composite formatting which can produce potential ambiguities, no?. I wonder, however, how many of those ambiguities could be eliminated by requiring that any expression more complicated than a simple member access be wrapped in parenthesis as well as braces. + +var s = $"x is less than y: {(x < y ? "true" : "false")}" + +> +> > * How are cultural settings applied to the implied ToString() calls that the holes get translated into? +> +> Proposal is to use the current culture. That's what .ToString() does. That's what String.Format() does if you don't pass extra arguments. + +Which also seems to be what every other language which incorporates this feature does. Right or wrong, it's consistent with both .NET and the rest of the ecosystem. + + +--- + +On Jul 31, 2014 at 5:27 PM @supercat wrote: + +**ljw1004 wrote:** +> That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code. It only ever looks at the {...} holes in the _literal_. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible. + +What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters, starting with a string, such that saying: + + st = String.Format(<$"There are "{count}" balls in the {jar}, at a price of "{pennies/100m,"0.00"}"."$>) + +would translate as: + + st = String.Format("There are {0} balls in the {{}jar}, at a price of {1:0.00}.", count, pennies/100m); + + +Basically, concatenating a brace-enclosed expression with the string would bump a counter *n* and insert "{*n*}". If the braces contained a comma and a string [literal or otherwise] the compiler would add a colon and the enclosed string after the number. Enclosing the main string in `<$ $>` would also cause brace characters within it to be escaped. + +--- + +On Jul 31, 2014 at 5:59 PM @lwischik wrote: + +**supercat wrote:** +> What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters + +Look in the OP, a few paragraphs down under "Javascript alternative". We discussed exactly this feature. We didn't like it because expanding it into a comma-separated list would not work for the simple case: + +```cs + var s = <$"There are {count} balls in the jar"$>; +``` + +Then it boils down to: how about having _both_ string interpolation which implicitly expands into a call to String.Format, _and_ a separate syntax (you suggest <$"..."$>, and we imagined $$"...") which expands into a comma-separated list and can only ever be used in function-call argument-list positions. + +It doesn't really seem worth the complexity. It seemed like few people would use $$"..." if they already had $"...". And having two similar features in the language would be a bit ugly. + +--- + +On Jul 31, 2014 at 6:03 PM @AaronLS wrote: + +**ljw1004 wrote:** + +> That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code. + +Yes you are correct, but if you follow the discussion you will see I'm very aware of this, yet there is a great deal of discussion here about providing named holes to something that evaluates at runtime similar to string.Format. I won't go into detail because there are numerous examples already in the thread of how this would be implemented and the feature parity it provides to the other features, as well as recognizing the differences. Just a couple posts before I distinguish the two approaches as Camp A and Camp B. + +> It only ever looks at the {...} holes in the _literal_. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible. + +I stated "you retrieve this from DB, and use string interpolation on it again". Clearly that's not a string literal, so clearly I'm referring to runtime interpolation. + +> I still disagree with your point 1. Consider: +> +> ```cs +> var msg = "There are { reds.count + blues.count } balls in the jar"; +> var msg = "There are {count} balls in the jar".Format( {count : reds.count + blues.count} ); +> ``` +> +> The first one reads more fluently. They're both doing exactly the same thing, but the second one introduces an extra layer of indirection that stops you reading it as easily, and forces you to invent a name "count" for something that you normally don't care to name. + +I will acknowledge you're points are in the context of compile time literals. So I won't argue the points related to runtime evaluation. + +Please read the points under the MS Developer's post(it's the second post) header "__Q. Expressions in holes?__" that gives some very good points of why this should not be allowed. One of the biggest ones being trying to parse C# from inside the string or provide intellisense will be very difficult. + +The first example reads "There are the 5 + 6 balls in the jar." The goal of the string is to express to the total number of balls in the jar. How you got to that total is not communicated in the final string, so including that detracts from the readability. If you include expressions in the string, what are you buying yourself beyond what we already do with string concatenation: + +``` +var msg = "There are { reds.count + blues.count } balls in the jar"; +var msg = "There are " + reds.count + blues.count + " balls in the jar"; +``` + +You didn't get yourself very much with this feature this way. You still have a string interrupted continuously with expressions. What if the expression were: + +``` +"There are { balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue) } balls in the jar." +//versus +"There are {total} balls in the jar.".Interpolate( + {total : balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue) } +``` + +The second string is much more readable. That level of indirection you pointed out is a good thing. It's a separation of concern. The string is concerned with presentation, the named params concerned with calculation. + + + +--- + +On Jul 31, 2014 at 6:33 PM @Halo_Four wrote: + +What it buys you is simplicity and compile-time checking. There isn't supposed to be a separation of those concerns, at least not in this case. If you wanted to make it easier to embed the results of complicated expressions that's when it would be appropriate to define a temporary variable to house said value. Even if the holes did permit arbitrary expressions of any degree of complexity that should still be considered the best practice for exactly the reason you specified, reducing clutter. + +```cs +var total = balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue) +var s = $"There are {total} balls in the jar."; +``` + +I do feel that named ```params``` is a neat feature that probably has a variety of uses. I also think that string.Format and other methods of composite formatting could use some forms of love in the framework. However those would be separate concerns from string interpolation which serves to fill the simple case. + +--- + +On Jul 31, 2014 at 6:42 PM @AdamSpeight2008 wrote: + +What if the type is __dynamic__? + +```c# +dynamic obj = ... +string x = "What about { obj.method }?" +``` + +Alternative to ```named params``` would be array (or collection) or Key-Value pairs. +```c# +var dict = { (key0 : value) , (key1 : value) , (key2 : value) } /* Key-Value pairs */ +``` + +Also [String.Format Diagnostic](https://stringformatdiagnostic.codeplex.com/) can / could be extended to include ```named holes```. +This will be provide the __compile-time warnings.__ + +--- + +On Jul 31, 2014 at 7:39 PM @AaronLS wrote: + +**Halo_Four wrote:** +> What it buys you is simplicity and compile-time checking. +``` +var msg = "There are { reds.count + blues.count } balls in the jar"; +var msg = "There are " + reds.count + blues.count + " balls in the jar"; +``` + +The first is no simpler than the second. That was the example I responded to, and it is no simpler. You've just traded +" for a {/} + + +--- + +On Jul 31, 2014 at 7:40 PM @richardtallent wrote: + +Making this blank and trying again to work around Codeplex bug + +--- + +On Jul 31, 2014 at 7:46 PM @richardtallent wrote: + +If we're *only* talking about improving string literals (and I hope we're not but I'll bite), the real "clutter" we're trying to avoid is the character overhead of `" +` and `+ "` when context-switching during string concatenation: + +``` +var msg = "There are " + (reds.count + blues.count) + " balls in the jar"; +``` + +So let's forget about interpolation and `String.Format` and get back to the core problem: + +**How can we context-switch more fluidly between string literals and expressions when performing string concatenation?** + +One solution might be for C# to generally support **postfix (RPN) notation of binary operators** after a _series of terms_ (used here for concatenation operators and strings): + +``` +var msg = "There are "reds.count" red balls in the jar"+; +``` + +Advantages of this approach: +* Whitespace around expressions like `red.count` is optional, so we can have them tight with the surrounding string literals, as above, or broken down by lines, indented, etc. We can also use parenthesis around terms if it makes things clearer in a particular situation. +* Full IDE support should be easy since the expressions are already *outside of the string literals*. +* This should be easy to implement as a compiler transformation to convert to infix. +* No overloading of new punctuation symbols, no new keywords. +* Postfix notation of binary operator could be useful in other situations (math, etc.). +* This is all inline concatenation, no calls to `String.Format` or similar methods. +* The compiler could even be smart and decide to use StringBuilder depending on the number of terms. +* It will make Forth developers and HP calculator users smile. +* I can't think of any ambiguity this would create with currently-valid C# language constructs. + +The formatting would be up to the developer. All of the below would be equivalent to the example above: + +``` +var msg = "There are " reds.count " red balls in the jar" +; +var msg = "There are "(reds.count)" red balls in the jar"+; +var msg = "There are " + reds.count + "red balls in the jar"+; // line breaks +``` + +--- + +On Jul 31, 2014 at 8:03 PM @AdamSpeight2008 wrote: + +How do we distinguish between the string +``` +"There are { reds.count + blues.count } balls in the jar" /* You actually want this string */ +``` +and the interpolated string +``` +"There are { reds.count + blues.count } balls in the jar" /* you want the interpolation of this string */ +``` + +Currently with __String.Format__ you have the context of the method to fall back on to determine "the meaning" of the string literal. + +__Side Note__ + +Enable diagnostics to change the styling of the text, so you could have it say colourise the holes. + +--- + +On Jul 31, 2014 at 9:21 PM @supercat wrote: + +**lwischik wrote:** +> **supercat wrote:** +> > What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters +> +> Look in the OP, a few paragraphs down under "Javascript alternative". We discussed exactly this feature. We didn't like it because expanding it into a comma-separated list would not work for the simple case: +> +> ```cs +> var s = <$"There are {count} balls in the jar"$>; + +If a programmer doesn't like typing `String.Format` add a mechanism to allow aliasing of free functions (something which IMHO should exist anyway: having to stick `Math.` in front of all the math functions in a block of code does nothing to improve readability). + +What would you suggest would be the equivalent if code wanted to call `someStringBuilder.AppendFormat(CultureInfo.InvariantCulture, ...)` with an interpolated set of parameters, or otherwise needed to do something other than default formatting? Code which is constructing HTML strings could benefit from interpolated string syntax, but only if it can use "." as a decimal separator regardless of culture settings. + +--- + +On Aug 11, 2014 at 11:39 PM @justinc wrote: + +I think you should just do $"..." for both languges. I would rather keep the contents of the strings as clean as possible so we don't have to litter it with \'s everywhere to use interpolation. + + var x = $"{foo} {bar} {baz}"; // better + +vs. + + var x = "\{foo} \{bar} \{baz}"; + +For one thing it's less characters and typing, but second it seems less cluttered visually. Also backslash already has a meaning in strings as an escape character, now it will have a meaning as both an escape character and an interpolation character? I don't like that. Also with the \ you'd have the ability to combine it with @ which changes the escape semantics already, using $ would let you reduce the complexity there I would think. Just say it's always @ mode plus interpolation? + +--- + +On Aug 12, 2014 at 2:41 PM @justinc wrote: + +With regards to localization I wanted to add an idea. + + var x = ProductName$"Roslyn {version}"; + +Which would both generate a string into the invariant string resource table with `"Roslyn {0}"` as well as generate the interpolation code like so: + + var x = String.Format(Resources.ProductName, version); + +I don't know what the ideal syntax would be exactly but the general idea is that you could specify the resource name as well as the default string all inline. + +--- + +On Aug 12, 2014 at 4:46 PM @lwischik wrote: + +**justinc wrote:** +> var x = ProductName$"Roslyn {version}"; +> Which would both generate a string into the invariant string resource table with `"Roslyn {0}"` as well as generate the interpolation code like so: +> var x = String.Format(Resources.ProductName, version); +> I don't know what the ideal syntax would be exactly but the general idea is that you could specify the resource name as well as the default string all inline. + +That could be scary. Imagine +```cs +var x = ProductName$"Roslyn v{major}.{minor}"; + ... "Roslyn v{0}.{1}" + ... "String.Format(Resources.ProductName, major, minor)" +``` + +Next I go ahead and localize my app, providing translations into other languages. Next I decide to revisit the English: +```cs +var x = ProductName$"Roslyn minor={minor}, major={major}"; +``` + +At this point the positional orders will flip and all other languages will be wrong and I likely won't notice. It's scary for (1) the authoritative English text to be embedded in the source code while all other languages are stored in .xlf files, (2) for the positional placeholders to be critically important despite a C# syntax which makes it look as though positional placeholders are unimportant. + + +--- + +On Aug 12, 2014 at 5:55 PM @mirhagk wrote: + +I don't think the language should directly address the localization. Rather use the aforementioned `Foo$"Roslyn {version}"` which calls `Foo` with parameters for it. Then a library could handle something like this (although as mentioned localization shouldn't embed the english in the code). + +--- + +On Aug 12, 2014 at 8:57 PM @AaronLS wrote: + + + +--- + +On Aug 13, 2014 at 5:36 PM @supercat wrote: + +**AaronLS wrote:** +> That was sort of the direction the discussions regarding named parameter lists were going. Instead of interpolation being a compile time language feature, it would be a runtime library feature(leveraging a new named param list language feature) + +I would like to see a named-parameter feature, perhaps using a special type + + struct NamedParameter { public String expressionText; public Type expressionType; public Object value; } + +and have a tag which would indicate that parameters should be placed into a `NamedParameter[]`. Unlike the parameter types `Object` and `Object[]`, both of which can be satisfied by an array, `NamedParameter[]` could only be satisfied by an array of that exact type, and `NamedParameter` only by a structure. + +--- + +On Aug 13, 2014 at 7:26 PM @Timwi wrote: + +Surely the obvious design for such a struct would be + +``` C# +struct NamedParameter { + public string Name { get; private set; } + public T Value { get; private set; } +} +``` + +which already exists (it’s called `KeyValuePair`), but if you’re going to have that, you might as well make it an `IDictionary`. Anything else would be stupid. + +--- + +On Aug 14, 2014 at 4:27 PM @supercat wrote: + +**Timwi wrote:** +> which already exists (it’s called `KeyValuePair`), but if you’re going to have that, you might as well make it an `IDictionary`. Anything else would be stupid. + +The `KeyValuePair` would be a fine choice if there were only one parameter, or if all parameters were of the same type, but neither condition will apply in most cases. `IDictionary` would have at least three deficiencies: + +1. It doesn't provide any information about the sequence in which keys are specified, nor allow for the possibility that a parameter may be specified more than once. +2. It doesn't provide any information about the type of a passed reference. +3. Hash tables perform better than arrays if they contain many items, and are queried many times--a situation which simply wouldn't apply here. + +BTW, with regard to point 2, there could actually be three types associated with a parameter. If a method which expects an `Animal[] critters` is given a `Cat[]`, and `critters[0]` identifies a `SiameseCat`, then the compile-time type of storage location `critters[0]` would be `Animal`, the actual type of *storage location* `critters[0]` would be `Cat`, and the type of the object identified by that storage location would be `SiameseCat`. In some cases, it could be helpful for the called method to know when the compile-time type and actual type of the storage location don't match, but providing that information to the method would require using Reflection at run-time and would probably not be worth the effort. + +--- + +On Aug 19, 2014 at 6:03 AM @sgjsakura wrote: + +HI all, I think + +1. the default culture for this feature should be InviarantCulture because it is a compile-time feature, which means you cannot use it for resource localization. The major usage may be for URI combination and internal diagnotics strings only. under such circumstance, using InviarantCulture culcure is more suitable. Actually I think there will be few chance to use string interplization combined with CurrentCulture because the format string is not localizable any all. + +2. Is there any equivalent design for duplicated placeholders like `string.Format("{0} is {0}", x)`? The runtime value of the parameter will be computed only once in this mode, but if you using `$"{x} is {x}"` I believe it will be computed twice. + +Nevertheless, if the feature is targeted for internal usage only (as I saied in the first point), this problem may be ignored because for most cases you will never display a value twice in URI or diagnotics string? + +Best Regards, + + +--- + +On Aug 21, 2014 at 2:40 AM @PauloMorgado wrote: + +**sgjsakura wrote:** +> 2. Is there any equivalent design for duplicated placeholders like `string.Format("{0} is {0}", x)`? The runtime value of the parameter will be computed only once in this mode, but if you using `$"{x} is {x}"` I believe it will be computed twice. + +What makes you think that? + + +--- + +On Aug 21, 2014 at 4:59 AM @supercat wrote: + +**PauloMorgado wrote:** +> What makes you think that? + +Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation. + +--- + +On Aug 21, 2014 at 9:44 AM @sgjsakura wrote: + +**PauloMorgado wrote:** +> **sgjsakura wrote:** +> > 2. Is there any equivalent design for duplicated placeholders like `string.Format("{0} is {0}", x)`? The runtime value of the parameter will be computed only once in this mode, but if you using `$"{x} is {x}"` I believe it will be computed twice. +> +> What makes you think that? + +I'm just think about this special case, it does not means I love the duplicated placeholder usage. + +--- + +On Aug 21, 2014 at 9:47 AM @sgjsakura wrote: + +**supercat wrote:** +> **PauloMorgado wrote:** +> > What makes you think that? +> +> Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation. + +Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use `$"{var x = ComputedValue()} is {x}"` =_= + +--- + +On Aug 21, 2014 at 11:26 AM @PauloMorgado wrote: + +**supercat wrote:** +> **PauloMorgado wrote:** +> > What makes you think that? +> +> Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation. + +Makes sense. + +--- + +On Aug 21, 2014 at 11:27 AM @PauloMorgado wrote: + +**sgjsakura wrote:** +> **supercat wrote:** +> > **PauloMorgado wrote:** +> > > What makes you think that? +> > +> > Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation. +> +> Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use `$"{var x = ComputedValue()} is {x}"` =_= + +That would be `$"{var x = ComputedValue(); x } is {x}"`, right? + +--- + +On Aug 21, 2014 at 11:37 AM @sgjsakura wrote: + +**PauloMorgado wrote:** +> **sgjsakura wrote:** +> > **supercat wrote:** +> > > **PauloMorgado wrote:** +> > > > What makes you think that? +> > > +> > > Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation. +> > +> > Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use `$"{var x = ComputedValue()} is {x}"` =_= +> +> That would be `$"{var x = ComputedValue(); x } is {x}"`, right? + +It seems that the declaration expression has its own value, please see the example `Console.WriteLine("Result: {0}", (int x = GetValue()) * x);` in the C# language feature list https://www.codeplex.com/Download?ProjectName=roslyn&DownloadId=894944 . + +Anyway, the detail for declaration expression is not important, I just want to say walkaround is aleady there :-) + +--- + +On Aug 22, 2014 at 1:50 AM @PauloMorgado wrote: + +**sgjsakura wrote:** +> It seems that the declaration expression has its own value, please see the example `Console.WriteLine("Result: {0}", (int x = GetValue()) * x);` in the C# language feature list https://www.codeplex.com/Download?ProjectName=roslyn&DownloadId=894944 . +> +> Anyway, the detail for declaration expression is not important, I just want to say walkaround is aleady there :-) + +Right! I was thinking of the semi-colon operator. + +--- + +On Aug 23, 2014 at 12:21 AM @AdamSpeight2008 wrote: + +```$"{var x = ComputedValue(); x } is {x}"``` + +Having arbitrary expression in the holes, is starting to look messy and confusing. + + +* Are those the same ```x``` or is the second coming from an outer scope? +* Without the aid of syntax highlighting it is just going to (at a quick glace) like a string. ![Image](http://i60.tinypic.com/72qan9.jpg) +The grey background section indicating the expression (which also has syntax highlighting), +* If arbitrary expressions are allowed, then it means it can embed another interpolation string inside of the hole of another interpolation string. + +``` +$"A$:={$" foo: {foo} bar: {bar}"} B$:={$" { var o = x.IsEven(); return o ?? "Even" : "Odd"; } "; } " +``` +_I don't think that would pass a code review_ + +--- + +On Aug 23, 2014 at 2:26 AM @AaronLS wrote: + +**AdamSpeight2008 wrote:** +> Having arbitrary expression in the holes, is starting to look messy and confusing. + +Agreed. If this is the direction that it's going to go, then I'd rather not have it all. If I could imagine one thing worse and less maintainable, than existing usages of string.Format with 10 holes, it's pushing all those expressions into the string. The string literal should express the goal. How you get to the goal should be seperated out and not embedded in the string. That was the reason for my previous proposal. + +I understand the goal is to make breaking in and out of the string "asdf" + something.Blah + "asda" simpler, but when you have such string concatenation so complex that you feel it needs to be simplified, your not buying yourself much just by eliminating a few "+" pairs. You still have a mess of expressions interupting the string. You've taken something bad and made it slightly less bad, but it is still bad. The opposite end of the sprectrum is string.Format, which pulls everything out, but the numbered holes require you to count through parameters to mentally map which express goes with which hole. __IMO string.Format is a heck of alot cleaner than concatenation, we just need to convert numbered holes into named holes, which is exactly what I was proposing.__ + +Otherwise you're beginning to venture into territory pretty well covered by templating engines already. For what it buys us, I don't think it's worth the complexity added to the language and compiler support. You also run into alot of confusing edge cases. For example, Razor at its essence is a string templating engine. It has some specialization in HTML, but it can be adapted to be used for non HTML string generation. If you've ever needed to conditionally output a class nested inside a another string you end up with some really fun to read stuff. + +``` +string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : "we are in a nested expression context so anyone reading must understand these quotes apply to the expression, not the outer string")"; + +``` + +--- + +On Aug 23, 2014 at 2:31 AM @AaronLS wrote: + +What's more, if you allowed that, then this is equally valid but exponentially horrible: + +``` +string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : $"we are in a nested $(numberOfExpressions > 1 ? "expressions": "expression") context so anyone reading $(hasUnderstanding ? "understands" : "does not understand") these quotes apply to the expression, not the outer string")"; +``` + +--- + +On Aug 23, 2014 at 3:17 AM @Halo_Four wrote: + +**AaronLS wrote:** +> What's more, if you allowed that, then this is equally valid but exponentially horrible: +> +> ``` +> string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : $"we are in a nested $(numberOfExpressions > 1 ? "expressions": "expression") context so anyone reading $(hasUnderstanding ? "understands" : "does not understand") these quotes apply to the expression, not the outer string")"; +> ``` + +We're not in a bubble. Plenty of other languages offer this functionality today and we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen. The fact that it can be horribly abused isn't a reason to not permit a feature. The question is whether developers might accidentally abuse it when intending to accomplish something else, like with switch-case fall through and the like. + +In this case, for Swift it appears that Apple has decided that an interpolation expression cannot contain another string literal, interpolated or not. The following is legal: + +``` +let b = true; +let x = "World"; +let y = "Dolly"; +let s = "Hello \(b ? x : y)!"; +``` + +While the following is not: + +``` +let b = true; +let s = "Hello \(b ? "World" : "Dolly")"; +``` + +I'd probably agree that it would be stepping too far to permit an interpolated string as an expression within an interpolated string. The decision appears to start at simple member expressions, which would cover the common cases, and explore outwards from there, which seems appropriate. + +I'm not completely for about ES6 but it does appear that Traceur does permit embedding template strings within template strings and your example is possible, which might be what you were thinking about considering you mixed in ES6's hole syntax instead of C#'s. + +--- + +On Aug 25, 2014 at 4:54 PM @AaronLS wrote: + +**Halo_Four wrote:** +> "we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen." + +1) How do you get to point C where you are looking at mitigating a problem without first visiting B where you acknowledge a potential problem? I can't believe you're actually saying you can evaluate how to mitigate a problem without first acknowledging the problem! How do you do that? That's liek saying "We can cure disease X without having actually disovered that disease X exists." Your argument is to say we should do no use case evaluation and you base that argument on the basis that other languages have made the same mistake and have to mitigate it. Let's do away with properties too while we're at it, and go back to using getSomething()/setSomething() like java does if that is a logical argument. + +2) Comparing use cases is not an arbitrary strawman. It was meant as a comparison against previous proposals which allow for such nesting in a much more clean and maintainable way. If you can accomplish such a complex case without the complexity of the code exploding geometrically then why ignore it? + +"The fact that it can be horribly abused isn't a reason to not permit a feature." + +3) True. Should we disallow a feature simply because it can be abused? No. What if it is EASY to abuse? Then we should consider an alternative implementation. It is absolutely a reason to consider an alternative and equally valuable feature. C# encourages a pit of quality. If you can provide a feature that accomplishes the same goals and encourages a pit of quality then why choose an implementation that doesn't? If one feature makes it difficult to write great code and easy to write terrible code, and another makes it easy then the former is better. + +Anyone who tries to accompolish similar nesting with the named holes approach when they aren't allowed to use expressions, will then make the next logical stab at it: + +``` +string someResult = $("Now welcoming {fullTitle}", + fullTitle : $("Sir Robin of {origin}", origin: isManInTights ? "Sherwood Forest" : "Knottingham" ) + ); +``` + +Accomplishes the same thing and the nesting can be repeated. Each string is easy to read and the named holes express the sementics: "Now welcoming {fullTitle}" "Sir Robin of {origin}". It is always better to for a language to express the "What" and abstract away the "How". We haven't pushed the "How", the expression far away, but just enough to significantly improve readability. You can still easily follow how the holes are being generated. __Now you don't have to implement intellisense inside string expressions!__ The worst you have to do compile-time is look for named holes and verify they match the named parameters. Imagine intellisense inside a string. How many times is it going to activate when you aren't really in a hole and it decides you typing "I " should insert some keyword that starts with I. Ever used SQL intellisense in SSMS and had it randomly replace your attempts to type an alias with some random keyword? + +You can keep nesting and the complexity of the code doesn't explode geometrically. + +To do the same cleanly when expressions are allowed requires self-discipline, and then you have to declare explaining variables to pull the expressions and nested string out, and build the holes before the actual goal strings. So you end up doing it all backwards: + +``` +string origin = (isManInTights ? "Sherwood Forest" : "Knottingham"); +string fullTitle = $("Sir Robin of {origin}"); +string someResult = $("Now welcoming {fullTitle}", +``` + +Granted, I have exaggerated here because it'd probably not be offensive to have inlined {origin} with the expression. But it certainly is nicer when all holes are named and improves the semantic readability of the string. I can very quickly see what the hole represents and not care how it is composed if when I'm reading the code. That's the What instead of the How. + +I'm so tired of seeing string.Format with 10 to 20 parameters at every job I have ever worked at in the last 10 years, and having to count through them everytime I have to debug their code. If you allow expressions inside strings then you are going to make it EASIER to write worse code. That's the opposite of a pit of quality. + +--- + +On Aug 25, 2014 at 6:05 PM @AdamSpeight2008 wrote: + +__AaronLS__ +> The worst you have to do compile-time is look for named holes and verify they match the named parameters. + +It already partial implemented in String.Format diagnostic. Just need extending to parse identifiers (which implemented already somewhere in the Roslyn source). +And you still be able to include alignment and format args as well. +_Any one want to fork it implement it?_ + +I said before that there some be a KeyValuePair Literal / operator __:=__ so it doesn't clash with label syntax. inline-if else and string formatting. +``` +var dic = { "A" := 1 , "B" := 2, "C" := 3 } /* A Collection of KeyValuePair +``` +__AaronLS__'s example +```c# +string someResult = $("Now welcoming {fullTitle}", fullTitle := $("Sir Robin of {origin}", origin:= isManInTights ? "Sherwood Forest" : "Knottingham" ) ); +``` + +__Scoping Rules__ + +Current the String.Format index the parameter array with the string format. Should interpolated string be the same? +1. Must Local first. Within the parenthesis of the interpolated string. +2. Next Higher scope +3. etc. +4. Not found? UnknownNamedHoleException + +Case-Sensitive in C#, Case-Insensitive in VB.net. + +--- + +On Aug 25, 2014 at 8:38 PM @AaronLS wrote: + +**AdamSpeight2008 wrote:** +> I said before that there some be a KeyValuePair Literal / operator __:=__ so it doesn't clash with label syntax. inline-if else and string formatting. + +Yes, I certainly don't intend my example to be a promotion of a particular syntax. Merely to show how clean your string literals are with the general named hole approach. There are alot of directions you could go with this and maintain the general principal of that proposed approach. Another possibility is using anonymous objects, which would be similar to how dotliquid's templating works. I personally think it would be nice if we minimized the amount of new syntax/operators. It could be done purely with existing syntax as a runtime API/feature, but probably wouldn't perform as well. Either way, I agree you certainly would want the diagnostics you mention to validate names at compile time, which could be leveraged in either case whether it was a runtime or compile time feature. In the former case you'd have to make the diagnostic ignore non-string literals since if it is a runtime feature then someone could be passing a dynamic string the same way you can use string.Format. Sometimes you have a string as a resource or template from a DB and you have to do the hole filling at runtime. + +``` +string someResult = string.Fill("Now welcoming {fullTitle}", new { + fullTitle = string.Fill("Sir Robin of {origin}", new {origin = isManInTights ? "Sherwood Forest" : "Knottingham"} ) + }); +``` + +I am starting to rehash discussions from couple weeks ago a bit. I'll just say there are some convincing arguments both for and against it only applying to string literals at compile time. I think I would be happy to have it either way. I simply feel strongly that experssions inside the holes should not be allowed. And also, some what less strongly, feel that names should only refer to explicit fields declared in the named parameters(or whatever syntax) rather than allowing other things like local variables, properties, and parameter names. Potential risks and alot of things like find all references and refactoring any of these names would not discover the references in the named holes(unless all of these toolings also being parsing string literals). Plus there is a potential risk in allowing holes to directly reference any identifier in current scope, I admit though, this risk is less of a concern when we are talking about string literals. + +Forcing named holes solves the references/refactoring problem since ```new { holeName= someParameter, anotherHoleName = someVariable }``` ensures refactorings of someParameter or someVariable will continue to work as they do today, and __won't require all of these various tools that do that kind of stuff to now implement parsing string literals to check if named holes reference a current scope variable/param/property__. + +--- + +On Aug 25, 2014 at 9:16 PM @Halo_Four wrote: + +**AaronLS wrote:** +> **Halo_Four wrote:** +> > "we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen." +> +> 1) How do you get to point C where you are looking at mitigating a problem without first visiting B where you acknowledge a potential problem? I can't believe you're actually saying you can evaluate how to mitigate a problem without first acknowledging the problem! How do you do that? That's liek saying "We can cure disease X without having actually disovered that disease X exists." Your argument is to say we should do no use case evaluation and you base that argument on the basis that other languages have made the same mistake and have to mitigate it. Let's do away with properties too while we're at it, and go back to using getSomething()/setSomething() like java does if that is a logical argument. +> +> 2) Comparing use cases is not an arbitrary strawman. It was meant as a comparison against previous proposals which allow for such nesting in a much more clean and maintainable way. If you can accomplish such a complex case without the complexity of the code exploding geometrically then why ignore it? +> + +No, my argument is quite the opposite. It is that since there are a number of existing languages that implement this feature that we can use the _actual data_ about the use cases to make appropriate design decisions rather than identifying the most complicated of edge cases and using that to argue against the proposed feature or to propose alternate syntax that would specifically address those use cases. + +>I think I would be happy to have it either way. I simply feel strongly that experssions inside the holes should not be allowed. And also, some what less strongly, feel that names should only refer to explicit fields declared in the named parameters(or whatever syntax) rather than allowing other things like local variables, properties, and parameter names. Potential risks and alot of things like find all references and refactoring any of these names would not discover the references in the named holes(unless all of these toolings also being parsing string literals). Plus there is a potential risk in allowing holes to directly reference any identifier in current scope, I admit though, this risk is less of a concern when we are talking about string literals. +> +> Forcing named holes solves the references/refactoring problem since new { holeName= someParameter, anotherHoleName = someVariable } ensures refactorings of someParameter or someVariable will continue to work as they do today, and won't require all of these various tools that do that kind of stuff to now implement parsing string literals to check if named holes reference a current scope variable/param/property. + +You keep forgetting that these are _not_ string literals. They are string templates and the compiler is fully aware of the holes and their contained expressions. The Roslyn parsers have done the difficult work and any tool built on that API will get refactoring capabilities out-of-the-box. + +I agree that embedding arbitrary expressions is a problem from a perspective of complexity and parsing ambiguity, particularly since the C# team intends to support composite formatting modifiers. The C# team seems to think that starting at locals and simple member access would be safe and I am inclined to agree with them. I do think that extended support for basic arithmetic, numeric indexers and simple method calls are probably just fine. I do like your idea of additional named holes which can be filled using a named argument syntax and think that could be used to expand the support to additional expressions, but that would be supplementary to the normal interpolation syntax and at the end of the day it still generates a normal call to `string.Format`. + +--- + +On Aug 25, 2014 at 9:59 PM @AdamSpeight2008 wrote: + +> You keep forgetting that these are not string literals. They are string templates and the compiler is fully aware of the holes and their contained expressions. The Roslyn parsers have done the difficult work and any tool built on that API will get refactoring capabilities out-of-the-box. + +As an example it could have a similar construction like the following + +```c# + +base IStr + +IStr_Text <: IStr + .Text : String! + +IStr_Hole <: IStr + .Alignment : IAlignmentArg + .Format : IFormatArg + +IStr_IndexHole + .Index : Int32 + +IStr_NamedHole <: IStr_Hole + .Name : String! + +Base InterpolatedParam + .Content : Object + +IndexedParam <: InterpolatedParam + .Index : Int32 +NamedParam <: InterpolatedParam + .Name : String! + +InterpolatedString + .Parts : List + .Params : List + .ToString() : String + { + var sb = new StringBuilder() + foreach( part In parts ) + { + match( typeof( part ) ) with + { + | IStr_Text As x => sb.Append( x.Text ) + | IStr_IndexHole As ih => sb.AppendFormat( params[ih.Index], ih.Alignment, ih.format) + | IStr_NamedHole As nh => sb.AppendFormat( params[nh.Name ], nh.Alignment. nh.format) + } + return sb.ToString() + } + } +``` +Obviously with improved handling of errors. + + +--- + +On Aug 25, 2014 at 11:42 PM @richardtallent wrote: + +For me, any solution must: +* __improve__ on plain old concatenation in terms of keystrokes and readability; +* allow arbitrarily complex expressions; +* allow for localization; and +* perform well +* avoid expressions embedded in strings, which are an anti-pattern IMHO (regardless which other language allows them). + +It seems there are two primary camps, and I'm not opposed to either, if the implementation meets the above criteria: + +__Camp 1: Literal and inline variable bits interspersed.__ + +This is basically how plain old string concatenation works now, what is being debated is an alternative syntax. + +I don't believe that sticking expressions *inside the string* is going to improve readability, parsing, or keystrokes _at all_. This expression-in-a-brace-in-a-string thing is overwrought and over-engineered, and the only excuse for it being "good" seems to be that other languages have similar Inception-style string-based expressions. + +The best way to improve concatenation of literals and arbitrary inline expressions is to _improve the syntax for string concatenation_, not re-invent the wheel within another wheel. I'm open to suggestions, but so far the best solution to me is to extend C# to allow __postfix notation__ of the "+" operator. This removes all of the superfluous "+" symbols, keeps literals and expressions contextually separate, and doesn't completely reinvent the wheel for interpreting expressions. + +White space is optional between the terms, and having the option to have line breaks in particular is useful for complex expressions. Color-coding would easily show the literal and variable bits, as it does now with concatenation. + +Example: + +``` +string someResult = "Thank you " name " for ordering " cart.Count + " product" (cart.Count>1 ? "s" : "") " from our store"+; +``` + +__Camp 2: Named placeholders for the variable bits, paired with expressions defining the names.__ + +This is basically String.Format, with named variables. Having explicit names and separate definitions is a Good Thing(TM) for clarifying intent, which is an advantage over normal string concatenation or other forms of inline expressions. Whether that advantage merits the potential down side of having to visually match the named variables with their definitions is highly subjective, and can also depend on the use case. It's certainly _at least_ better than the current String.Format syntax (index numbers), and the more complex a variable's expression is, the more likely having a named reference will be preferable to having the expression inline. + +For this approach, I'm still a fan of extending C# to support an arbitrary number of _named_ parameters ("params on steroids"): + +``` +string someResult = String.Format("Thank you {name} for ordering {count} product{pluralizeCartCount} from our store", + name: name, + count: cart.Count, + pluralizeCartCount: cart.Count > 1 ? "s" : "" +); +``` + +The precise syntax could be done different ways and could take advantage of C#'s upcoming feature for establishing key/value pairs, but overall this approach provides a good separation of the variable placeholders and their definitions without an overload of extra fluff to, say, instantiate and populate a dictionary. And, unlike the first solution, the first argument of String.Format would not need to be a literal, thus allowing localization. + +If I had to choose between the two approaches, I would pick the second. + +--- + +On Aug 26, 2014 at 12:55 AM @Halo_Four wrote: + +**richardtallent wrote:** + +> It seems there are two primary camps, and I'm not opposed to either, if the implementation meets the above criteria: +> +> __Camp 1: Literal and inline variable bits interspersed.__ +> +> ``` +> string someResult = "Thank you " name " for ordering " cart.Count +> " product" (cart.Count>1 ? "s" : "") " from our store"+; +> ``` +> +> __Camp 2: Named placeholders for the variable bits, paired with expressions defining the names.__ +> +> ``` +> string someResult = String.Format("Thank you {name} for ordering {count} product{pluralizeCartCount} from our store", +> name: name, +> count: cart.Count, +> pluralizeCartCount: cart.Count > 1 ? "s" : "" +> ); +> ``` + +You missed the third camp: proper compile-time string interpolation. It's already been decided as being a fancy syntax on top of `string.Format` converted at compile-time. It seems that the only question remaining by the C# team was the extent of the expressions permitted within the holes. + +```cs +string s = $"Hello {person.Name}, you were born on {person.BirthDate:G}."; +``` + +--- + +On Aug 26, 2014 at 1:23 AM @AdamSpeight2008 wrote: + +__richardtallent__ +So your advocating / of the opinion that the named holes should restrict scoping to within the bounds of the ```String.Format( )``` +```c# +String.Format( " { 0,-3 } {Song_Name, 32} [Artist,32} ", Index, Song_Name; nameofsong, Artist: performer ) +``` +- - - +If "we" extend the scoping out a bit further, it would be possible to just write the following. +```c# +var TrackListing = from CD in Music + from track in CD.Tracks + select $("{track.Number,-3): {track.Song_Name. 32} {track.Artist. 32}") +``` +Since we can parse the interpolation string into parts. extending both the type-checker an the syntax highlighting to the inside would back things clearer. +Especially if semantic highlighting is used. ```track.`` would be the same color so you could see the flow of the data. +- - - +```c# +var s = $("{$track.Number,3}: {1, 32} {trackArtist,32)", trackArtist:= track.Artist, track.Song_Name) +``` +Hole +1. An extended scoped named hole ( akin to current variable scoping rules ) +aka Identified Hole (refers to an Identifier) +2. An Indexed Hole +3. Named Hole + +Arbitrary Expression are confined to the Indexed Hole and Named Hole paramaters. + +--- + +On Aug 26, 2014 at 2:18 AM @richardtallent wrote: + +**Halo_Four wrote:** + +> You missed the third camp: proper compile-time string interpolation. + +If you put a _placeholder_ at the position in the string where you want the evaluated result to occur, it's Camp 2. If you the _expression to be evaluated_ there, it's Camp 1. + +So your example falls within Camp 1, you're just using different syntax and implementation method than I'm proposing. + +I'm proposing a tweak to reduce the syntactic overhead of the binary "+" operator on string concatenation as an _alternative_ to the $"" approach. + +Both put the expressions _positionally_ within the string at the insertion point -- mine just does so without having to write a whole new template language and creating oddball issues like escaping curly brace characters. + +> It seems that the only question remaining by the C# team was the extent of the expressions permitted within the holes. + +Yes, if only _placeholders_ are allowed, it would be a Camp 2 approach, but if _expressions_ are allowed (referencing in-scope variables, adding logic, etc.), it's Camp 1. + +Here's your example, and my own preference for a Camp 1 solution: + +```cs +string s = $"Hello {person.Name}, you were born on {person.BirthDate}."; +string s = "Hello "person.Name", you were born on "person.BirthDate"."+; +``` + +I did remove the explicit string format to illustrate the syntax similarity better... I'll grant that a template language can handle ToString() in fewer characters, but it wouldn't be able to use a _variable_ format, such as the user's locale or an application setting. + +One advantage to my example is that when the expressions get more complicated than accessing property, you can add line breaks or parenthesis sets to make the logic clear: + +```cs +string s = "Hello "person.Name", you were" + (person.BirthDate < DateTime.Today.AddDays(-1) ? " not" : "") + " born yesterday."; +``` + + +--- + +On Aug 26, 2014 at 3:00 AM @richardtallent wrote: + +**AdamSpeight2008 wrote:** + +> So your advocating / of the opinion that the named holes should restrict scoping to within the bounds of the ```String.Format( )``` + +Yes, just like `{0}` has no meaning to anything but the String.Format method now. + +> String.Format( " { 0,-3 } {Song_Name, 32} [Artist,32} ", Index, Song_Name; nameofsong, Artist: performer ) + +I think for a given call you'd have to _either_ do indexed positions (0, 1,...) or named (Song_Name), not mix and match them, so the overload that uses `params[]` or the named parameter equivalent is called, but yes. + +> If "we" extend the scoping out a bit further, it would be possible to just write the following. +> var TrackListing = from CD in Music from track in CD.Tracks select $("{track.Number,-3): {track.Song_Name. 32} {track.Artist. 32}") + +Interesting use case. + +Let me assume for a minute that we use "$" as a simple shortcut for String.Format. I think a cleaner approach would be (whitespace optional of course): + +```c# +var TrackListing = from CD in Music from track in CD.Tracks select $("{num,-3): {song,32} {artist,32}", + num: track.Number, + song: track.Song_Name, + artist: track.Artist +); +``` + +--- + +On Aug 26, 2014 at 3:03 AM @Halo_Four wrote: + +**richardtallent wrote:** +> **Halo_Four wrote:** +> +> > You missed the third camp: proper compile-time string interpolation. +> +> If you put a _placeholder_ at the position in the string where you want the evaluated result to occur, it's Camp 2. If you the _expression to be evaluated_ there, it's Camp 1. +> +> So your example falls within Camp 1, you're just using different syntax and implementation method than I'm proposing. +> + +Concatenation could be one implementation of string interpolation, beneath the hood anyway. I think that getting RPN into the compiler would be a non-starter. The C# team already considered concatenation syntax in the first post of this thread, including with simplification of syntax, and dismissed it. + +I still refer to the [May 27th C# team design notes](https://roslyn.codeplex.com/discussions/546467) where the syntax was nailed down to the syntax that I used in my example. I do hope that they would weigh in on the subject in one way or another in an upcoming meeting just to give these conversations something more concrete to concentrate on. + +I do like elements of AaronLS's named placeholder syntax as a way to expand upon the syntax particularly where embedded expressions would be unwieldy even though it is only a minor difference from declaring a temporary variable and interpolating that. + +--- + +On Aug 26, 2014 at 3:32 AM @richardtallent wrote: + +**Halo_Four wrote:** + +> I think that getting RPN into the compiler would be a non-starter. + +Alas. + +> I do like elements of AaronLS's named placeholder syntax as a way to expand upon the syntax + +Me too, and I was hoping the ship hadn't totally sailed. I'm just not a fan of expressions embedded within strings. I'm far more comfortable with named placeholders. + +I think of it in MVC-like terms -- using named holes give you clean separation between the view (the string template), the model (the key/value collection of named parameters), and controller (the expressions that determine said keys' values). + +In terms of implementation of named parameters, you could extend `params` to support named arguments (itself useful in other contexts), or you could simply do the same sort of compile-time rewrites to traditional String.Format (with indexed holes) or to plain old concatenation as the current solution proposes. + +--- + +On Aug 26, 2014 at 9:51 PM @JavedSaqib wrote: + +Interpolation is some kind of an operation or? Why not to use an operator for doing it? ":" + +Operation could be checked at compile time. + + +``` +var x = new +{ + World = "World", + Count = 42, +} + +"Hello {World}. What is the Truth? {Count}!" : x +``` + +Or + +``` +"Hello {World}. What is the Truth? {Count}!" : new { World = "World", Count = 42 } +``` + +Or + +``` +"Hello {World}. What is the Truth? {Count}!, {Foo}" : str => new { World = "World", Count = 42, Foo = str.Length } +``` + +Or + +``` +"Hello {World}. What is the Truth? {Count}!, {Foo}" : str => new []{ "World", 42, str.Length } +``` + +Or + +``` +"Hello {0}. What is the Truth? {1}!, {2}" : str => new []{ "World", 42, str.Length } +``` + +Or all togehter ;) + +--- + +On Aug 27, 2014 at 6:39 AM @AdamSpeight2008 wrote: + +A Interpolated String consists of two basic classifications. __Text__ and __Arg__ (Hole). +```vb.net +MustInherit Class FormatStringBlock + Overridable Function SourceText() As String + End Function +End Class + +NonInheritable Class Text + Inherits FormatStringBlock +End Class + +MustInherit Class Arg + Inherits FormatStringBlock + ReadOnly Property Alignment As FormatStringAlignment + ReadOnly Property Formatting As FormatStringFormatting +End Class +``` +Let's say that the arg (hole) could be one of three forms. +1. Indexed ```{ 0 }``` +2. Named ```{ name }``` +3. Identified ```{$id}``` + +```vb.net +NonInheritable Class Indexed + Inherits Arg + Public ReadOnly Property Index() As Integer +End Class + +NonInheritable Class Named + Inherits Arg + Public ReadOnly Property Name() As String! +End Class + +NonInheritable Class Identified + Inherits Arg + Public ReadOnly Property Identifier() As Identifier +End Class +``` +The parameter list (potentially) supplied is not as simple like it is for ```String.Format``` since it now there is the possibility of it being a named parameter. + +```vb.net +Class FormatStringArg + ReadOnly Property Value() As Object +End Class +Class NamedArg + Inherits FormatStringArg + ReadOnly Property Name() As String +End Class +``` +So let's now implement the function ```.ToSting()``` that output the result of an interpolated string. + +```vb.net +Class InterpolatedString + + Dim Blocks As New List(Of Block) + Dim _Args As New List(Of FormatStringArg) + + Const _ArgCountLimit = 1000000 + + Function ToString() As String + Dim _NamedArgs = Args.OfType(NamedArg) + Dim sb As New Text.StringBuilder + ForEach block In Blocks + Select Case TypeOf(block) + Case Text As tb : sb.Append(tb.Text) + Case Indexed As ib + Dim InRange = ib.Index.IsBetween(0,ib.Count) + Select Case InRange + Case True : sb.Append( Args( ib ).Value ) + Case False When ib.Index >= ArgCountLimit : Throw Exceptions.ArgCountLimitExceed() + Case Else : Throw Exceptions.IndexOutOfRange() + End Select + Case Named As nb + Dim d = _NamedArgs.Where(Function(b) b.Name = nb.Name) + If Not d.Any() Then Throw Exceptions.NamedArgNotFound() + Dim res = d.First + sb.AppendFormat(res.Value, block.Alignment, block.Formatting + Case Identified As Id + Dim res = Compilier.FindIdentifier(Id.Identifier) + If res Is Nothing Then Throw Exceptions.IdentifierNotFound() + sb.AppendFormat( res.Value, block.Alignment, block.Formatting ) + End Select + Next + Return sb.ToString() + End Function + +End Class +``` +It is relatively simple to implement, the only complex part is the locating of Identifiers. But the compiler only has support for that, it just a matter of accessing that functionality. I haven't implemented the parsing of an interpolated string (yet) because that aspect. it should be that hard to extend my current implementation to parse named and identified arg (holes). + +--- + +On Aug 27, 2014 at 1:43 PM @JavedSaqib wrote: + +**JavedSaqib wrote:** +> Interpolation is some kind of an operation or? Why not to use an operator for doing it? ":" +> +> Operation could be checked at compile time. +> +> +> ``` +> var x = new +> { +> World = "World", +> Count = 42, +> } +> +> "Hello {World}. What is the Truth? {Count}!" : x +> ``` +> +> Or +> +> ``` +> "Hello {World}. What is the Truth? {Count}!" : new { World = "World", Count = 42 } +> ``` + +It could be also somthing like a object intilizer (in this case special string intilaizier) for compile time. +For runtime the operator would be sufficient. + +``` +"Hello {World}. What is the Truth? {Count}!" +{ + World = "World", + Count = 42, +} +``` + +Operation with the string on the right side, could lead to infer the type and give intellisense for the string. + +``` +x : "Hello {World}. What is the Truth? {Count}!" +``` + +One important thing should be, that it should be possible to interpolate type safe a interpolated string. + +Example: + +``` +var str = "Hello {World}. What is the Truth? {Count}!" +{ + World = "World", + Count = "42", +} + +str : new { World = "New World" } // correct +str : new { Worldx = "New World" } // compile time error, because interpolated string does not match the placeholder metadata. +``` + +So for dynmaic string you must convert/cast into interpolated string. + +``` +var stringFromDatabase = "Some string with some placeholders {PLACEHOLDER1} {PLACEHOLDER2}"; + +var interStr = stringFromDatabase << new []{ PLACEHOLDER1 = typeof(String), PLACEHOLDER2 = (typeof(Char))} +``` + +Or infer from type + +``` +var myType = new +{ + PLACEHOLDER1 = "Some Text", + PLACEHOLDER2 = 'X', +}; + +var interStr = stringFromDatabase << myType +``` + +Or an interpolated string or a string which should be interpolated can be seen as a type with extended fields/poperties + +Example: + +``` +var someString = "Some Text {A}, {B}"; + +someString.A = "Hello"; +someString.B = "World"; +``` + +Some one agrees with that? I think it is more natural for C# (maybe VB). + +--- + +On Aug 27, 2014 at 6:06 PM @AdamSpeight2008 wrote: + + have a nearly complete implementation of a parser.for an interpolated string of the form $(" ",) +See a previous post for rough grammer. +It just require the.functions that parses Identified and Named hole completing. +Will post when internet connect is.better (when the fix it) + +__Edit__ + +I opened a discussion topic over on String.Format Diagnostic which contain the core function. + +[Parse Interpolated String](https://stringformatdiagnostic.codeplex.com/discussions/564764) + +--- + +On Sep 1, 2014 at 12:13 AM @AdamSpeight2008 wrote: + +The is an interesting possible of the above format of string interpolation:- [template function](https://roslyn.codeplex.com/discussions/564366) + +--- + +On Oct 6, 2014 at 6:12 PM @Vincent3000 wrote: + +I agree that some improvements over ugliest string.Format were done (BTW, who architected that "Format"? Is he still alive?). But why again we see all those backslashes? Are you still sleeping in C-language era? + +``` +var a = `My name is {name}` +``` + - isn't it enough?? (with backticks as string limiter and just curlies for substitution) + +--- + +On Oct 6, 2014 at 6:57 PM @agat50 wrote: + +I find 'dapper style' string interpolation (with dynamic data type) awesome. Absolutely understand {0}..{N} haters, string names are more useful, but don't think it must be some new language feature. Just another overload .Format with another style of format string would be ok. +Kinda: +```c# +string a = "hello world"; +Console.WriteLine( + string.Format( + "'{A}'.Count = {B}", + new{ + A = a, + B = a.Length + } + ) +); +``` + +Or, with http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2406863-make-it-possible-to-call-string-format-like-my : + +```c# +Console.WriteLine( + "{C:G}: '{A}'.Count = {B}".Format( + new{ + A = a, + B = a.Length, + C = DateTime.Now + } + ) +); +``` + + + +--- + +On Oct 6, 2014 at 7:03 PM @Halo_Four wrote: + +**Vincent3000 wrote:** +> I agree that some improvements over ugliest string.Format were done (BTW, who architected that "Format"? Is he still alive?). But why again we see all those backslashes? Are you still sleeping in C-language era? +> +> ``` +> var a = `My name is {name}` +> ``` +> - isn't it enough?? (with backticks as string limiter and just curlies for substitution) + +These meeting notes are obsolete. The conclusion during the [May meeting](https://roslyn.codeplex.com/discussions/546467) was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. + +```cs +string result = $"Hello, my name is {name} and I was born on {dob:D}."; +``` + +Which would be translated to the following: + +```cs +string result = String.Format("Hello, my name is {0} and I was born on {1:D}.", name, dob); +``` + +As for the backslash syntax, that was actually what Apple Swift had chosen to use to escape the "holes" in the interpolated string. It is appropriate for a language of c heritage as C# is. + + +--- + +On Oct 6, 2014 at 7:15 PM @tom103 wrote: + +**agat50 wrote:** +> I find 'dapper style' string interpolation (with dynamic data type) awesome. Absolutely understand {0}..{N} haters, string names are more useful, but don't think it must be some new language feature. Just another overload .Format with another style of format string would be ok. +> Kinda: +> ```c# +> string a = "hello world"; +> Console.WriteLine( +> string.Format( +> "'{A}'.Count = {B}", +> new{ +> A = a, +> B = a.Length +> } +> ) +> ); +> ``` + +I agree, but it's not the plan... I think this approach is better because it enables localization and is consistent with the existing `String.Format` ([actually you can already have this today with library support](https://github.com/thomaslevesque/NString#stringtemplate)). +Baking it into the language does have a few benefits, though: +- Better performance (because there is no need to use reflection to get the values) +- Complex expressions in the placeholders, rather than just simple names +- Compiler checking of the expression + +--- + +On Oct 6, 2014 at 7:15 PM @lwischik wrote: + +**Halo_Four wrote:** +> These meeting notes are obsolete. The conclusion during the [May meeting](https://roslyn.codeplex.com/discussions/546467) was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. `$"hello {customer.name}"` + +We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer `"hello \{customer.name}"` for C#, but still `$"hello {customer.name}"` for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB). + +When it does come out, we'll want people to __play around with it in practice__ to see how it feels. And for people to play with it in practice before voicing further strong opinions :) + +The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. + +-- Lucian from VB/C# LDM + + +--- + +On Oct 6, 2014 at 7:25 PM @AdamSpeight2008 wrote: + +__@lwischik__ +Why can't they share the same syntax / style? + +--- + +On Oct 6, 2014 at 7:26 PM @Halo_Four wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > These meeting notes are obsolete. The conclusion during the [May meeting](https://roslyn.codeplex.com/discussions/546467) was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. `$"hello {customer.name}"` +> +> We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer `"hello \{customer.name}"` for C#, but still `$"hello {customer.name}"` for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB). +> +> When it does come out, we'll want people to __play around with it in practice__ to see how it feels. And for people to play with it in practice before voicing further strong opinions :) +> +> The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. +> +> -- Lucian from VB/C# LDM + +You could ship Roslyn with a money-printing printer and people would voice "strong opinions" regarding that they still had to load the thing with paper. + +Interesting on the redesign. I didn't see it mentioned in the September meeting notes. Honestly either way works for me, although I'm curious as to how the function prefixing (e.g., `loc$""`) might come into play, if it will at all. + +--- + +On Oct 6, 2014 at 7:32 PM @AdamSpeight2008 wrote: + +What if you want the characters ```{``` and ```}``` in your interpolated string? Especially the c# version. As ```\{``` would suggest that but from the example it doesn't. + +In the VB version it is simply ```{{``` and ```}}``` respectively + +Ctrl + Left Arrow also helps when going to the beginning of the string. Maybe an additional shortcut Ctrl + Alt + Left Arrow? to jump to the start? + +--- + +On Oct 6, 2014 at 7:41 PM @AdamSpeight2008 wrote: + +__@Halo_Four__ ```$``` would have to be both a prefix and also a binary operator like ```-``` + +Which would be funky as in VB it also is a type identifier for string. ```Dim mystring$ ``` + +--- + +On Oct 6, 2014 at 7:41 PM @tom103 wrote: + +**lwischik wrote:** +> The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. + +That's a good point, but I think I still prefer the "$" prefix... Will this syntax still allow the use of a custom interpolator function? + +--- + +On Oct 6, 2014 at 7:55 PM @agat50 wrote: + +I agree, compile check is good (despite on it's quite hard to implement full code edit in such expressions in IDE imho). Actually, i'm more concerned about one thing - lets face it, real var names are often long and ugly (not to mention complex expressions). Format string will transform rather to code than format. Don't think it'll be easy to read. {0}...{N} are short, and it's real advantage. Anyway, seemingly it's more about user-friendly ide supporting. + +--- + +On Oct 6, 2014 at 7:58 PM @Halo_Four wrote: + +**AdamSpeight2008 wrote:** +> What if you want the characters ```{``` and ```}``` in your interpolated string? Especially the c# version. As ```\{``` would suggest that but from the example it doesn't. +> +> In the VB version it is simply ```{{``` and ```}}``` respectively +> +> Ctrl + Left Arrow also helps when going to the beginning of the string. Maybe an additional shortcut Ctrl + Alt + Left Arrow? to jump to the start? + +You wouldn't be required to escape the curly braces as they would be considered content and not a part of the interpolation, e.g. + +```cs +string s = "The coords of the point are: { \{p.x}, \{p.y} }."; +``` + +would be converted to: + +```cs +string s = String.Format("The coords of the point are {{ {0}, {1} }}.", p.x, p.y); +``` + +--- + +On Oct 6, 2014 at 8:00 PM @nmgafter wrote: + +**AdamSpeight2008 wrote:** +> What if you want the characters ```{``` and ```}``` in your interpolated string? Especially the c# version. As ```\{``` would suggest that but from the example it doesn't. + +If you want { in your string you type {. + +If you want } in your string you type }. + +What could be simpler? + + +--- + +On Oct 6, 2014 at 8:16 PM @AdamSpeight2008 wrote: + +__@nmgafter__ +It kinda breaks this where ```\\``` indicates that you want to use character \ in the string. ```\{``` It also moves C# and VB.net strings even further apart. + +You can't tell if the user actually meant that string ``` "The coords of the point are: { \{p.x}, \{p.y} }."``` or they mean interpolated string. +The use of prefix helps to differentiate the two cases. + +--- + +On Oct 6, 2014 at 8:31 PM @Halo_Four wrote: + +**AdamSpeight2008 wrote:** +> __@nmgafter__ +> It kinda breaks this where ```\\``` indicates that you want to use character \ in the string. ```\{``` It also moves C# and VB.net strings even further apart. +> + +That is standard and expected behavior for any c-heritage language going back over 40 years. The `\` is the important part of the escape sequence. Notably, `\{` is currently illegal syntax so to co-opt it for interpolation cannot break any existing code. The difference here with VB isn't that important because BASIC's heritage does not have escape sequences in strings, save for the doubling of double-quotes in order to insert a single double quote. + +> You can't tell if the user actually meant that string ``` "The coords of the point are: { \{p.x}, \{p.y} }."``` or they mean interpolated string. +> The use of prefix helps to differentiate the two cases. + +Depends on where you're looking. If you came across `{p.x}` in the middle of a normal string you'd have to glance back to the beginning to figure out if it were intended to be interpolated or just a part of the literal. But if your goal is to know whether the string is interpolated or not then a prefix would help in determining that quickly. Either way I think syntax coloring in the IDE would be the most useful tool in visually interpreting the string. + +My opinion isn't that strong one way or the other. I originally favored prefixing over escaping but after toying with Apple Swift I think that escaping feels as natural. + +--- + +On Oct 6, 2014 at 8:41 PM @AdamSpeight2008 wrote: + +You shouldn't have rely on syntax highlighting to determine the meaning. + +> That is standard and expected behaviour for any c-heritage language going back over 40 years. + +May be that's the problem. C# isn't C. + +--- + +On Oct 6, 2014 at 8:56 PM @Halo_Four wrote: + +**AdamSpeight2008 wrote:** +> You shouldn't have rely on syntax highlighting to determine the meaning. + +And you wouldn't need to, but it'll be more obvious. Otherwise you'd have to scan somewhere else to see what the programmer's intent was, whether the syntax was a prefix or an escape sequence. In my opinion I think it would be more common for a programming to be scanning the contents and to see an expression hole and to want to know whether or not that represented an interpolation rather than to want to know this without having read any of the string. + +> +> > That is standard and expected behaviour for any c-heritage language going back over 40 years. +> +> May be that's the problem. C# isn't C. + +That it's not, but its syntax is deliberately based on that of C, including escape sequences. + +--- + +On Oct 6, 2014 at 9:49 PM @VladD wrote: + +**lwischik wrote:** +> The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. + +In my opinion, even if this notation favours the _writers_, it makes the code harder to _read_. Prefix makes it obvious from the very beginning that the string is actually not a string constant, but a complicated expression. With the `\{` notation, the reader might well be halfway through the string content when realizing that the string is actually not a constant string. One can even not notice the difference if the string is long and not fully visible in the edit area (which opens nasty possibilities for the hidden "surprise" code in holes). + +Another confusion could be about why the code + + const string s = "Innocently-looking string here, and here, and 1000 more characters here, and at last \{ this }"; + +fails to compile. With prefix that would be obvious from the first glance. + +I am in the prefix camp. + +--- + +On Oct 6, 2014 at 10:22 PM @AdamSpeight2008 wrote: + +Also if use the existing style of ```String.Format``` for String.Interpolation you can share a vast majority of the required code, the only difference is the code to recognise identifiers rather arg index. That is also is made easier by that fact most of the exists already in roslyn. + +``` + Head ::= '$' '"' + Tail ::= '"' + IString ::= Head Body Tail + Body ::= (TextBlock* ArgHole+ TextBlock*)+ + ArgHole ::= '{' Identifer (',' Alignment )? (';' Formatting )? '}' + TextBlock ::= "{{" | "}}" | + Alignment ::= ('+'|'-')? NumericCharacter+ +Formatting ::= etc +``` +The grammar for Identifiers is a little more complex +``` + Identifier ::= NonEscapedIdentifier [ TypeCharacter ] | EscapedIdentifier +NonEscapedIdentifier ::= < IdentifierName but not Keyword > + EscapedIdentifier ::= [ IdentifierName ] + IdentifierName ::= IdentifierStart [ IdentifierCharacter+ ] + IdentifierStart ::= AlphaCharacter | UnderscoreCharacter IdentifierCharacter + IdentifierCharacter ::= UnderscoreCharacter | + AlphaCharacter | + NumericCharacter | + CombiningCharacter | + FormattingCharacter + AlphaCharacter ::= < Unicode alphabetic character (classes Lu, Ll, Lt, Lm, Lo, Nl) > + NumericCharacter ::= < Unicode decimal digit character (class Nd) > + CombiningCharacter ::= < Unicode combining character (classes Mn, Mc) > + FormattingCharacter ::= < Unicode formatting character (class Cf) > + UnderscoreCharacter ::= < Unicode connection character (class Pc) > + IdentifierOrKeyword ::= Identifier | Keyword +``` + +As an additional benefit you could also get more compile-time warnings / errors. via [String.Format Diagnostics](https://stringformatdiagnostic.codeplex.com/) + +--- + +On Oct 6, 2014 at 10:47 PM @PauloMorgado wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > These meeting notes are obsolete. The conclusion during the [May meeting](https://roslyn.codeplex.com/discussions/546467) was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. `$"hello {customer.name}"` +> +> We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer `"hello \{customer.name}"` for C#, but still `$"hello {customer.name}"` for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB). +> +> When it does come out, we'll want people to __play around with it in practice__ to see how it feels. And for people to play with it in practice before voicing further strong opinions :) +> +> The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. +> +> -- Lucian from VB/C# LDM + +That sounds a lot like a reason after the fact. A problem created for the solution. + +So, I'm on C# 5.0 (or VB - which version is it now?) happily coding away and I start writing `var key = "super_dooper_` - oops! Looks like I need to add a few dynamic values to this. And several, because, otherwise, I'd just use string concatenation. + +Bummer, I need to go all the way back to add `string.Format(`. + +C# 6.0 comes out and, same situation. But now I'm lucky enough to be a C# developer and I can code away without having to go back and add something to the beginning of the string. I feel sorry for those poor VB developers that still have to do that. + +Now comes the time read code and `Dim key = $"super_dooper_{customer.LastName}_{customer.FirstName}_other_stuff"` is immediately perceived as an interpolated string without needing to read the string contents. On the other hand , `var key= "super_dooper_\{customer.LastName}_\{customer.FirstName}_other_stuff"` requires me to scan the string contents until I find the first `\{` or until the end just to know if it's interpolated or not. + +There's only one reason I can think of for `\{` being somewhat better than a prefix, but I'm not going to say what it is. + +I'm really just trying to understand why is this such a good idea. + +--- + +On Oct 6, 2014 at 11:17 PM @supercat wrote: + +**Halo_Four wrote:** +> That is standard and expected behavior for any c-heritage language going back over 40 years. The `\` is the important part of the escape sequence. Notably, `\{` is currently illegal syntax so to co-opt it for interpolation cannot break any existing code. The difference here with VB isn't that important because BASIC's heritage does not have escape sequences in strings, save for the doubling of double-quotes in order to insert a single double quote. + +The normal idiom for C is that a backslash in a string literal turns itself and the next character or, if the next character is an "x", "X", or a digit, the next few characters, into a single character that would otherwise be hard to type. I can't think of any cases where any other C-like language prior to Swift used a backslash within a string to effectively switch to a different processing mode, nor do I see any advantage to such a structure versus moving the non-literal content outside the quotes. + +Saying `Header = $"Coordinates: ["(X)","(Y)"]";` would be nearly as concise as the format using `\{` but avoid any confusion about what is part of the string literal and what isn't. Actually, what I'd like would be a means by which a list of literal strings and parenthesis-delimited values would be transformed so as to be equivalent to `"Coordinates: [{0},{1}]",(X),(Y)`, at least in contexts where the a suitable overload had a parameter of type `String` which was tagged with an attribute indicating it should accept that style of string. + +--- + +On Oct 6, 2014 at 11:23 PM @AdamSpeight2008 wrote: + +I agree with Paulo, why different syntaxs for the same feature? Why the double effort? + +--- + +On Oct 7, 2014 at 12:02 AM @lwischik wrote: + +**AdamSpeight2008 wrote:** +> I agree with Paulo, why different syntaxs for the same feature? Why the double effort? + +The implementations necessarily have to be pretty much completely different between C# and VB. + +Both languages have an "incremental parser" which only reparses the bits of the parse tree affected by where you're typing right now. In VB this can easily be line-based like it's always been. C# doesn't have lines so it has more complicated rules. + +Also VB already has a stateful lexer+parser, and needed it to deal with XML literals. We haven't got there in the prototype yet, but it might be reused for string interpolation. C# has never had a stateful lexer+parser. + + +--- + +On Oct 7, 2014 at 12:14 AM @Halo_Four wrote: + +I probably come off as being much more of an advocate either way than I am. I'm pretty ambivalent as to the syntax for this feature. Honestly I'm more defending the escape-sequence syntax more out of sheer Devil's Advocacy towards the arguments against it. :) + +I can see advantages and disadvantages to all of the major proposed syntaxes. Escape sequences fit with the existing language structure. To "interpolate" an existing string requires little additional effort as the contents of the existing string could never run afoul of the new syntax. That said, at first glance it may be difficult to discern whether or not a string literal is really a literal or a calculated value, which can be problematic. + +The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation. + +Backticks are like prefixes in that they are immediately clear to be different from string literals, and they could help solve what I think might be one of the more common use cases of string interpolation which is inline non-localized pluralization by allowing expressions to contain non-interpolated string literals using double-quotes. + +That list is certainly not exhaustive, just what I thought up while doing the dishes. :) + +That said, it seems that the Roslyn team thinks that escape sequences are the way to go for the time being, enough so that CTP4 will have that syntax for string interpolation. I'd love to see their notes from last month as it may shed some additional light onto the thinking of the language team. I'm sure that release will be immediately followed with a surge in opinions on this forum again. + +--- + +On Oct 7, 2014 at 12:16 AM @Halo_Four wrote: + +**lwischik wrote:** +> **AdamSpeight2008 wrote:** +> > I agree with Paulo, why different syntaxs for the same feature? Why the double effort? +> +> The implementations necessarily have to be pretty much completely different between C# and VB. +> +> Both languages have an "incremental parser" which only reparses the bits of the parse tree affected by where you're typing right now. In VB this can easily be line-based like it's always been. C# doesn't have lines so it has more complicated rules. +> +> Also VB already has a stateful lexer+parser, and needed it to deal with XML literals. We haven't got there in the prototype yet, but it might be reused for string interpolation. C# has never had a stateful lexer+parser. + +Technical reasons aside, I'd argue that they're different languages so they should have different syntax to fit in with the rest of the language. I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that? + +--- + +On Oct 7, 2014 at 12:35 AM @AdamSpeight2008 wrote: + +Argument for prefix syntax;- I reckon that on balance you'll spend more time reading and understanding code, rather than writing it. + + +--- + +On Oct 7, 2014 at 12:37 AM @lwischik wrote: + +**Halo_Four wrote:** +> I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that? + +Depends how it turns out when we come to implement it in VB. My guess is that multiline stuff will be hard to mix with string interpolation, because of the "incremental parsing" issues, and so we'd restrict $"..." to be single-line in VB at least for now. It's something that can be relaxed in future. C# interpolated strings will also be single line and I'm not sure how it could be made multi-line. + + + +--- + +On Oct 7, 2014 at 12:44 AM @PauloMorgado wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that? +> +> C# interpolated strings will also be single line and I'm not sure how it could be made multi-line. + +With a `@` prefix! :) + +--- + +On Oct 7, 2014 at 12:49 AM @Halo_Four wrote: + +**AdamSpeight2008 wrote:** +> Argument for prefix syntax;- I reckon that on balance you'll spend more time reading and understanding code, rather than writing it. + +Quite. The first statement I wrote under "prefixes" was intended to convey that it was easier to discern at a glance and I have rewritten it to be clearer. + +At this point I guess we'll see where things falls after CTP4. I can't imagine that there is a lot of room left for reworking features. + +--- + +On Oct 7, 2014 at 12:58 AM @Halo_Four wrote: + +**PauloMorgado wrote:** +> **lwischik wrote:** +> > **Halo_Four wrote:** +> > > I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that? +> > +> > C# interpolated strings will also be single line and I'm not sure how it could be made multi-line. +> +> With a `@` prefix! :) + +I do think think that if the two were to be combined that only prefixing would make sense as combining the symbols `@` and `$` at least declare a pretty specific intent. Since `@` shuts down escape sequence parse it wouldn't make any sense at all to then rely on it for interpolated verbatim strings. + +However, I actually kind of like having the limitation of not being able to combine verbatim strings and interpolated strings if only to discourage programmers from writing really long interpolated strings. At the end of the day, syntax aside, this feature encourages a number of bad practices. I'd honestly rather see easier support in VS for writing interpolated resources, e.g. including named placeholders in the resource strings which the project item converts into functions accepting those values as parameters. + +--- + +On Oct 7, 2014 at 6:42 AM @nmgafter wrote: + +**PauloMorgado wrote:** +> Now comes the time read code and `Dim key = $"super_dooper_{customer.LastName}_{customer.FirstName}_other_stuff"` is immediately perceived as an interpolated string without needing to read the string contents. On the other hand , `var key= "super_dooper_\{customer.LastName}_\{customer.FirstName}_other_stuff"` requires me to scan the string contents until I find the first `\{` or until the end just to know if it's interpolated or not. + +Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there. + +This is a common reaction to new language features. When people first encounter a new language feature they despair that it does not have more syntactic ceremony so they can easily see where the new feature is used in the code. Then after they've gotten used to it they bemoan all the ceremony around the new feature, and wonder why it couldn't have been simpler. + +> There's only one reason I can think of for `\{` being somewhat better than a prefix, but I'm not going to say what it is. + +Actually, a prefix would have made the feature easier to parse. We went with this implementation because it is the way we believe the feature should be because it is easier to use when typing. + +--- + +On Oct 7, 2014 at 6:55 AM @PauloMorgado wrote: + +**nmgafter wrote:** +> Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there. + +You've got a point there. +> > There's only one reason I can think of for `\{` being somewhat better than a prefix, but I'm not going to say what it is. +> +> Actually, a prefix would have made the feature easier to parse. We went with this implementation because it is the way we believe the feature should be because it is easier to use when typing. + +I have no doubt about that. + +> This is a common reaction to new language features. When people first encounter a new language feature they despair that it does not have more syntactic ceremony so they can easily see where the new feature is used in the code. Then after they've gotten used to it they bemoan all the ceremony around the new feature, and wonder why it couldn't have been simpler. + +I agree with you in general but not in this particular case: me! + +Because I'm not against it, just because, I've been trying to sell it to myself and I'm almost there. I'm sure the reasons are scattered around these posts and I just didn't read them with enough attention, but I'm already 99.9% there. + +--- + +On Oct 7, 2014 at 9:50 AM @Przemyslaw wrote: + +@lwischik, nmgafter +I'll support other who were asking for custom interpolator - how do you see this playing with ```\{``` syntax? If the main argument is "it is easier to write, you don't need to jump to the beginning of the string", then it should come as postfix syntax. This would be weird. Only ++ and -- use postfix in c#. And these are applied to identifiers, which are not that long and thus don't require scanning line far to the right. This is not the case with strings, which will be much longer. If you want to use prefix syntax (I really like the way JS implements this), then the whole argument that you don't need to go to the beginning is out of the table. Anyway - the golden rule to remember is "write once - read many times". IMHO ```\{``` syntax goes against this. + +**nmgafter wrote:** +> Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there. + +Assuming that expression is long and contains few string concatenations, etc - sane developers will split this into few lines of code, so that it is easier to read. I can do this with string concatenation, so that each concatenated part takes own line if necessary. I cannot do this with interpolated string - it will be one liner. To be fair, string interpolation by its nature will remove much of the "ceremony", making the whole expression much easier to read. Will need to play with it to see how this is balancing. +Why do I care if this is interpolated string or not? Really good question. I need to play with the feature to see if this will be actual issue or not, but I can see potential pain point - debugging. Not sure if I am correct, but I think, the autos, locals and watch windows will show expanded content of the string. It might not be that obvious where the error is, if it is inside one of the interpolated expressions. It might be helpful to see that particular string was interpolated to find the root cause of a bug faster. However, this can be addressed at the IDE level, in debugger. + +--- + +On Oct 7, 2014 at 12:49 PM @Halo_Four wrote: + +**nmgafter wrote:** +> Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there. + +Well, this is probably a very uncommon scenario but I have worked with code where it was important to know whether or not the string was an actual literal as the callee required an interned string reference and would fail otherwise. Not to get into too much detail but it was a binary-based logging mechanism where the format strings were written exactly once. + + +--- + +On Oct 7, 2014 at 2:28 PM @KathleenDollard wrote: + +Please use the prefix approach in C#: + +- Lucian, on other features you favor reading over writing, that also applies here +- Neal, of course we care whether it is interpolated. Unless you've done magic, interpolation will be enough slower to matter a lot in a tight loop. And the concatenation will always be an option to consider if you discover perf issues. +- I'm dyslexic on slashes. The less you use backslash the better +- I'm cross language. The less you introduce differences, the happier I am +- $" is elegant, and because C# coders know @" they will always immediately know "special string, I wonder what kind of special string". +- "\{" looks like you're escaping to INCLUDE the brace, not drop to special processing +- The debugging Find RegEx for "This is a (snail|\{)" is way better than "This is a (snail|\\\{)" (did I get that right?) +- The RegEx above indicates one of the ways backslash is used in RegEx, to include a special character in the search - this is the exact opposite of what is intended by the interpolation proposal - with the same character. Yes, I know RegEx also escapes in a similar manner (escaping into special ops) such as \S, but that is (annoying and) escaping non-punctuation. Think RegEx, especially in search isn't important? After fixing breakpoints (LOVE YOU GUYS) making non-wimpy find is the next most important thing to do, and RegEx is already there as the basis (create a drop dead RegEx Find peek editor, and your done). + +I was initially against this feature because it hides a string method usage and therefore can hide a perf problem. I would have preferred an interpolation that rested inside String.Format for clarity in what's really happening. + +So, let us have the prefix so we can at least find these things! I can search code for $" if I find a boatload of time in String.Format scattered throughout my code. + +We'll survive either way. But please don't tell us we can work this out after it appears in a CTP if that isn't realistic. If we're close enough to lock down that there won't be time to change, please reconsider now. And if we can get a CTP 4.1 that just has this feature, and maybe getter-only auto-props, it is absolutely worth it. + +Kathleen + +--- + +On Oct 7, 2014 at 2:58 PM @KathleenDollard wrote: + +One more thing.. + +Can some show the string creation that will create a string that when passed to a RegEx will result in this output, assuming the backslash approach? + +"flskj sagsd {sdfdf, sfdsdf}" + +OK, so maybe the answer is, you have to use the at sign in front to get a meaningful RegEx string, and that's OK since you have to use the @ sign today. + +var name = "Lu"; +var regexString1 = "asdf \{name}"; +var regexString2 = @"asdf \{name}"; + +Both are passed through regex. The first results in "asdf Lu" and the second results in an attempt to match "asdf" plus any upper case, right? + +var name = "Fred"; +var regexString1 = "asdf $\{name}"; +var regexString2 = @"asdf $\{name}"; + +Passed to RegEx, the first is "asdf Fred" and the second is "asdf" plus a substitution to a group named "name" + +I actually don't think this is as strong as the readability and performance arguments, because @ has always fundamentally altered output. + +But most of the RegEx escapes aren't legal prior to this proposal, so you're led into the pit of success for using the backslash in the right place. This will no longer be true with the backslash proposal for string interpolation. + +Kathleen + + +--- + +On Oct 7, 2014 at 4:36 PM @tom103 wrote: + +I agree with everything Kathleen said + +--- + +On Oct 7, 2014 at 5:52 PM @btjdev wrote: + +Another vote against backslash, for all the reasons everyone else has already stated. As a big fan of the already-existing @-strings, I think there's a good precedent there for prefixes, and I agree that it seems more readable (which I view as being more important than easier to type). Also, backslash would add confusion when combined with @ - would the backslash be treated as a literal or not in that case? + +I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture. So, thanks to KathleenDollard for covering some of the scenarios that seem to be getting overlooked; I'd love to see more discussion of using this in scenarios where you aren't building a string to be displayed to a user. + +--- + +On Oct 7, 2014 at 6:02 PM @CoolDadTx wrote: + +For the prefix vs question I'm voting for a prefix. You'll break code if simply try to figure out what I want based upon some arbitrary characters in a string. I happen to already use {xyz} in a custom formatting library we use. Assuming that would never appear preceded by a slash (or anything else) is just wrong. A prefix makes it clear (like the verbatim string). + +--- + +On Oct 7, 2014 at 6:10 PM @lwischik wrote: + +**btjdev wrote:** +> I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture. + +The only things that display differently with InvariantCulture are DateTime and floats. + +DateTime's default InvariantCulture display is generally wrong for API usage like pathnames and regex and uris and serialization -- instead you'd use a format string like `"hello \{ DateTime.Now :o}"` or similar. + +As for floats, InvariantCulture alone is a bit dodgy since it sometimes flips into scientific notation. I think you'd either do a combination of InvariantCulture and format string `INV$"hello { val : f4}"`, or you'd do it through the format string alone `"hello \{ val : "0\\.00"} "`. + + + +--- + +On Oct 7, 2014 at 6:13 PM @Halo_Four wrote: + +**CoolDadTx wrote:** +> For the prefix vs question I'm voting for a prefix. You'll break code if simply try to figure out what I want based upon some arbitrary characters in a string. I happen to already use {xyz} in a custom formatting library we use. Assuming that would never appear preceded by a slash (or anything else) is just wrong. A prefix makes it clear (like the verbatim string). + +The backslash syntax doesn't break any existing code. Including `\{` in a non-verbatim string is currently not legal C#, you are required to escape the backslash, e.g. `"\\{foo}"`. Interpolation won't be permitted with verbatim strings with or without backslashes so it's also not a concern there. + + +--- + +On Oct 7, 2014 at 9:31 PM @supercat wrote: + +**Halo_Four wrote:** +> The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation. + +Why is it necessary to use escapes for holes? Why not simply put the hole content outside the quoted part of the string? It's not presently legal to precede or follow a string literal with a value enclosed in parentheses, so `$"Coordinate is ["(x)","(y)"]";` should be fine. If someone wants to space it more widely as `$"Coordinate is [" (x) "," (y) "]";` that would be fine too. I don't like the idea of having non-literal stuff inside quotes without a good reason, and I don't think I've seen a good reason. + +--- + +On Oct 7, 2014 at 9:56 PM @Halo_Four wrote: + +**supercat wrote:** +> **Halo_Four wrote:** +> > The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation. +> +> Why is it necessary to use escapes for holes? Why not simply put the hole content outside the quoted part of the string? It's not presently legal to precede or follow a string literal with a value enclosed in parentheses, so `$"Coordinate is ["(x)","(y)"]";` should be fine. If someone wants to space it more widely as `$"Coordinate is [" (x) "," (y) "]";` that would be fine too. I don't like the idea of having non-literal stuff inside quotes without a good reason, and I don't think I've seen a good reason. + +You've mentioned your syntax before and it gained no traction either in the forum or with the C# team so as far as I can tell it is not in contention. I'm more interested in discussing what the C# team is likely to actually implement. + +The majority of languages which offer string interpolation do include the placeholder directly within the string literal. It is what is expected of the feature, for better or worse. + + +--- + +On Oct 7, 2014 at 11:08 PM @AdamSpeight2008 wrote: + +__@supercat__ +An issue with that format ``` $"Coordinate is [" (x) "," (y) "]"; ``` is the use of parenthesis. Parenthesis are used to force the order of evaluation of an expression, put some forget that there is an implicit parenthesis around the expression. ``` ( (x) + (y) ) ```, thus valid to include around the expression. +Also how would you distinguish between your form of string interpolation and an malformed line, that contained a string eg ``` "Coordinate is [" + (x) + "," + (y) "]" ; ``` + +--- + +On Oct 8, 2014 at 12:07 AM @supercat wrote: + +**AdamSpeight2008 wrote:** +> __@supercat__ +> An issue with that format ``` $"Coordinate is [" (x) "," (y) "]"; ``` is the use of parenthesis. Parenthesis are used to force the order of evaluation of an expression, put some forget that there is an implicit parenthesis around the expression. ``` ( (x) + (y) ) ```, thus valid to include around the expression. + +I'm not quite sure what you mean. If the syntax requires the existence of at least one set of braces, what difference would more make? + +> Also how would you distinguish between your form of string interpolation and an malformed line, that contained a string eg ``` "Coordinate is [" + (x) + "," + (y) "]" ; ``` + +I don't think putting an expression enclosed in parentheses next to a string literal is ever valid under existing rules, is it? There could be ambiguity if a `+` appears before the last string literal, but I would think the result of the expression would either be the same as if the last literal were part of the interpolated string or else would refuse compilation. + +--- + +On Oct 8, 2014 at 1:29 AM @PauloMorgado wrote: + +Kathleen, + +I've been very firmly, as usually, on the same stand you are. However, I'm now almost 100% in favor of the proposed solution for C#. + +In C# we have two types of string literals (§2.4.4.5): __regular string literals__ and __verbatim string literals__. + +Verbatim string literals are interpreted verbatim with the only exception of _quoted-escape-sequences_. + +Even if the implementation of interpolated strings used a prefix it would not make sense for a string being both interpolated and verbatim because, if it is verbatim, it can't be interpolated. At this point I'm, let's say, 75% in favor of the prefix. + +Now, having verbatim string literals out of the way, let's see what you can write in a regular string literal and what does the compiler do with it. To put it simple, regular string literals are strings (how unexpected!) of characters enclosed by `"`s. But the compiler doesn't just take those characters and throw them on the IL string. If you write `\"`, the compiler will convert it to ". You can also write control characters and characters specified by their hexadecimal code and/or their Unicode code (§2.4.4.4). The common denominator of all these interpretations by the compiler is that they are prefixed by `\` character. + +If you look at the how the space character is expressed in hexadecimal form (`\x0020`) you might think of it as an hexadecimal number prefixed by `\x`. Now try to think of it as an escaped expression (`x0020`) and that all escaped sequences are prefixed by `\`, being a control character, an Unicode specification, a `'` or a `"`. And holes (`{expr}`) are also escaped sequences (`\{expr}`). + +Does that make sense? I think so! + +Does that look like good old C#? I think so! + +I'm now 100% in favor of `\{expr}` for declaring holes in interpolated strings. + +As for regular expressions, you already have that problem if you are looking for, say, new lines. The regular expression is `\n` but you have to write it either as a verbatim string literal (`@"\n"`) or as regular string literal where you have to escape the escape character (`"\\n"`). + +--- + +On Oct 8, 2014 at 2:47 AM @KathleenDollard wrote: + +Paulo, + +… interpolated and verbatim… + +I am not suggesting that we allow the same string to be a verbatim string and a interpolated string. One or the other, agreed. + +No matter what we do, we now have three kinds of strings. We cannot change this and the more we hide the fact, the more trouble we will be in + +1. verbatim strings +2. “normal” strings which support escaped syntax, which escape the thing immediately following – the thing following may be a multi-digit number, or a Unicode character string, but it is still the thing that immediately follows. Yes, arguable “x” is (in context) punctuation, but it is a widespread prefix for a hex number, not a new thing. +3. interpolated strings + +1 and 2 are only slightly different. I am not aware of any fundamental rewriting that occurs to result in different behavior in relation to the .NET framework. + +3 is entirely different. It is not a simple string assignment. It is a call to a .NET framework format method posing as a string assignment. + +I spent time down the path of twisting my head around this being escaping on steroids. It’s not. Escaping has a history. Holes have a history. They are different things. Escaping exists in many languages and even a small amount of action in odd Unicode (uppercase). This is immediately data with flexible formatting, and I’m quite sure there will be pressure for expressions in a future version (as is appropriate). + +Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need? + +Dim filepath = $"{drive}:\{path}\{file}.{ext}" + +And where did this feature land for culture. A lot was said, and I don’t know the outcome. + +Kathleen + + + +--- + +On Oct 8, 2014 at 3:02 AM @Halo_Four wrote: + +**KathleenDollard wrote:** +> Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need? +> +> Dim filepath = $"{drive}:\{path}\{file}.{ext}" +> + +```cs +string filepath = "\{drive}:\\\{path}\\\{file}.\{ext}"; +``` + +You'd need most of them anyway just to escape the escape sequences. + +> And where did this feature land for culture. A lot was said, and I don’t know the outcome. + + +Defaults to current culture, the same as String.Format does when not passed a specific culture. This is consistent with every implementation of string interpolation that I've seen in other languages as well. + + +--- + +On Oct 8, 2014 at 3:45 AM @bondsbw wrote: + +I second the questions above about custom interpolation. I know it was never a promised feature, but at least with the $" syntax it seemed more plausible. + +--- + +On Oct 8, 2014 at 10:19 AM @PauloMorgado wrote: + +**KathleenDollard wrote:** +> Paulo, +> +> … interpolated and verbatim… +> +> I am not suggesting that we allow the same string to be a verbatim string and a interpolated string. One or the other, agreed. +> +> No matter what we do, we now have three kinds of strings. We cannot change this and the more we hide the fact, the more trouble we will be in +> +> 1. verbatim strings +> 2. “normal” strings which support escaped syntax, which escape the thing immediately following – the thing following may be a multi-digit number, or a Unicode character string, but it is still the thing that immediately follows. Yes, arguable “x” is (in context) punctuation, but it is a widespread prefix for a hex number, not a new thing. +> 3. interpolated strings +> +> 1 and 2 are only slightly different. I am not aware of any fundamental rewriting that occurs to result in different behavior in relation to the .NET framework. +> +> 3 is entirely different. It is not a simple string assignment. It is a call to a .NET framework format method posing as a string assignment. +> +> I spent time down the path of twisting my head around this being escaping on steroids. It’s not. Escaping has a history. Holes have a history. They are different things. Escaping exists in many languages and even a small amount of action in odd Unicode (uppercase). This is immediately data with flexible formatting, and I’m quite sure there will be pressure for expressions in a future version (as is appropriate). +> +> Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need? +> +> Dim filepath = $"{drive}:\{path}\{file}.{ext}" +> +> And where did this feature land for culture. A lot was said, and I don’t know the outcome. +> +> Kathleen + +It might be a call to a .NET framework format method. + +--- + +On Oct 8, 2014 at 2:20 PM @KathleenDollard wrote: + +HaloFour- thanks for the clarifications on the file path and culture. I think that is the correct culture to use. It carries the same issues and benefits as the current usage and that's appropriate. + +I think I was unclear. There is no backwards compatibility break. With this proposal, people will need to think of strings differently, rather than thinking of a new kind of string. + +Whether it happens all the time or some of the time (compiler optimization), these new strings may use a much difference approach in creation, and may is enough here. I've been meaning to do some profiling for both perf and memory usage of String.Format vs. simple concat, and because I'm not very good at that, it will take a lot of time I don't really have. So I looked it up and found numbers from 2009 and 2010 that differ significantly. + +http://geekswithblogs.net/BlackRabbitCoder/archive/2010/05/10/c-string-compares-and-concatenations.aspx +http://weblogs.asp.net/jevgeni/appending-string-in-c-string-vs-string-format-vs-stringbuilder + +BlackRabbit's numbers were only 20% slower and have a negligible difference in memory. That surprised me, because I anticipated numbers more like Jevgeni's numbers. There may be a CLR version difference as well, but the main difference is that BlackRabbit is working to isolate the impact of single line concatenation, where Jevgeni is building up a large string. Jevgeni's numbers are not very important here, as building up a string is obviously the job for String Builder. + +So, can someone from the team comment on the range of performance difference we might see in statements that are not likely to be optimized, but that wouldn't be using Format. I'm not quite sure what that would be, but I don't need to see perf numbers, just a general sense. + +If the difference is 20% or less, I'm going to downplay the performance argument. 20% will be a lot in a very small number of cases (assuming StringBuilder cases aren't relevant). It won't be measurable in most applications, and if that's the case, overlooking usage on code review of performance focused code isn't a big deal (optimization is sometimes appropriate during development, that is simply good habits). + +I still strongly favor the prefix for all the reasons I stated Tuesday. But, if the perf hit for single line usage of format vs. concat is in the 20% range, the argument of a hidden perf problem is pretty weak and limited. + +Kathleen + + + +--- + +On Oct 8, 2014 at 3:08 PM @lwischik wrote: + +**KathleenDollard wrote:** +>I've been meaning to do some profiling for both perf and memory usage of String.Format vs. simple concat + +In planning this feature we anticipated that the compiler would be able to optimize where necessary... + +* If there are no format specifiers, the compiler could just call .ToString() itself on the holes and then String.Concat. This will be more efficient in all cases than String.Format since it avoids the runtime parse step that String.Format has to do, and it can also avoid boxing, e.g. String.Format("{0}", an_integer). + +* If there are no holes then the compiler can use a string literal. + +It's a question of how much we want to hard-code knowledge about the functioning of String.Format into the optimizing step. + + +-- Lucian Wischik, VB/C# language team + + +--- + +On Oct 8, 2014 at 5:26 PM @btjdev wrote: + +**lwischik wrote:** +> **btjdev wrote:** +> > I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture. +> +> The only things that display differently with InvariantCulture are DateTime and floats. +> +> DateTime's default InvariantCulture display is generally wrong for API usage like pathnames and regex and uris and serialization -- instead you'd use a format string like `"hello \{ DateTime.Now :o}"` or similar. +> +> As for floats, InvariantCulture alone is a bit dodgy since it sometimes flips into scientific notation. I think you'd either do a combination of InvariantCulture and format string `INV$"hello { val : f4}"`, or you'd do it through the format string alone `"hello \{ val : "0\\.00"} "`. + +Thank you, nice analysis and summary of the language culture scenarios. + +Whoever is implementing the compiler translation for this should verify that Code Analysis rule `CA1305: Specify IFormatProvider` will be OK with whatever IL is generated under the hood. + +--- + +On Oct 8, 2014 at 6:04 PM @Halo_Four wrote: + +**lwischik wrote:** +> As for floats, InvariantCulture alone is a bit dodgy since it sometimes flips into scientific notation. I think you'd either do a combination of InvariantCulture and format string `INV$"hello { val : f4}"`, or you'd do it through the format string alone `"hello \{ val : "0\\.00"} "`. + +Just to clarify did you mean to include double-quotes in the format specifier for the second example? Shouldn't that be: + +```cs +float val = 123.456f; +var s = "hello \{ val : 0.00 }"; +``` + +I assume that the whitespace around the variable name and format specifier are also optional? + +--- + +On Oct 9, 2014 at 12:14 AM @supercat wrote: + +**KathleenDollard wrote:** +> I still strongly favor the prefix for all the reasons I stated Tuesday. But, if the perf hit for single line usage of format vs. concat is in the 20% range, the argument of a hidden perf problem is pretty weak and limited. + +If performance is an issue, I would think that in cases where the maximum length of the string is known, it would be faster to create an array of suitable size on the stack, build the string into that array, use the `String(char*, int, int)` constructor to build the final string, and return that. Such an approach would eliminate the need for any heap object allocations other than the final string itself. It would be necessary to have versions of number-formatting methods which would be capable of writing to such an object (if desired, to make things safe, code could use a struct containing a fixed-sized array except in cases where things might need to be longer, in which case code could simply create and combine `String` objects. + +I also wonder what the practical performance implications would be if an interpolated string generated a `struct` with one member for each hole, and then passed that struct to a generic method which would on the first time it was given a particular struct type use Reflection to generate code to format that combination of parameters, and on subsequent calls would use the earlier-built method. If a particular combination of parameter types is only used once, the time spent generating the type would be wasted, but on the flip side if a piece of code is only used once the time to execute it generally won't matter. + +--- + +On Oct 9, 2014 at 4:49 PM @KathleenDollard wrote: + +supercat: Just looking relative to the current usage. + +--- + +On Oct 9, 2014 at 5:45 PM @supercat wrote: + +**Halo_Four wrote:** +> The majority of languages which offer string interpolation do include the placeholder directly within the string literal. It is what is expected of the feature, for better or worse. + +The languages I'm aware of with string interpolation (e.g. PHP) don't have the same sort of type system or compilation/execution model as C#/.NET. Perhaps there are some I'm unaware of which do? In particular, are there any in which the bindings associated with string interpolation are fixed when the string is encountered by the compiler, rather than when the string is evaluated at runtime? + +Also, can the expressions in holes contain quotation marks? An inability to do include expressions that involve string literals would seem an annoying limitation, but if they can contain quotation marks I would think that would make life pretty miserable for a syntax highlighter. Has anyone figured out what sort of algorithm a syntax highlighter should be expected to use? I would expect it to be pretty nightmarish, but maybe it's not so difficult as I imagine? + +--- + +On Oct 9, 2014 at 7:29 PM @lwischik wrote: + +**Halo_Four wrote:** +> Just to clarify did you mean to include double-quotes in the format specifier for the second example? Shouldn't that be: +> ```cs +> float val = 123.456f; +> var s = "hello \{ val : 0.00 }"; +> ``` + +(1) C# obviously can't allow arbitrary format specifiers, since they can include any characters at all. We decided to restrict specifiers to either identifiers or string literals. + +(2) I had been trying to write a format specifier which would always print the character '.' regardless of current culture. My use of "0\.00" was a naive attempt at that. I discover I was wrong. As far as I can tell now, it is _impossible_ just with format specifiers to ensure the separator will be a dot. + +Is that right? That's worse than I thought. I'll go back to the LDM. + + +--- + +On Oct 9, 2014 at 7:35 PM @lwischik wrote: + +**supercat wrote:** +> The languages I'm aware of with string interpolation (e.g. PHP) don't have the same sort of type system or compilation/execution model as C#/.NET. Perhaps there are some I'm unaware of which do? In particular, are there any in which the bindings associated with string interpolation are fixed when the string is encountered by the compiler, rather than when the string is evaluated at runtime? + +Looking at the wikipedia page http://en.wikipedia.org/wiki/String_interpolation Nemerle, Swift and Scala would be similar to C#/.NET. + +> Also, can the expressions in holes contain quotation marks? + +They can't in Swift (nor newlines). I think we figured it was possible to support quotation marks in C#. As for VB, we haven't done the implementation for string literals yet, but they're conceptually the same as XML literals with holes in VB. They are achieved by having a stateful lexer+parser, i.e. it's either in VB or XML mode, and can switch between the two. That's why you're allowed to put XML inside expressions in VB inside holes in XML inside expressions in VB inside holes in XML inside ... + +> Has anyone figured out what sort of algorithm a syntax highlighter should be expected to use? I would expect it to be pretty nightmarish, but maybe it's not so difficult as I imagine? + +You can look at the parser source code! It's all open-source. + + +--- + +On Oct 9, 2014 at 9:03 PM @Halo_Four wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > Just to clarify did you mean to include double-quotes in the format specifier for the second example? Shouldn't that be: +> > ```cs +> > float val = 123.456f; +> > var s = "hello \{ val : 0.00 }"; +> > ``` +> +> (1) C# obviously can't allow arbitrary format specifiers, since they can include any characters at all. We decided to restrict specifiers to either identifiers or string literals. +> +> (2) I had been trying to write a format specifier which would always print the character '.' regardless of current culture. My use of "0\.00" was a naive attempt at that. I discover I was wrong. As far as I can tell now, it is _impossible_ just with format specifiers to ensure the separator will be a dot. +> +> Is that right? That's worse than I thought. I'll go back to the LDM. + +Oof! That seems _way_ overly restrictive. Why does it need to be so restrictive? Honestly the only characters that I think might be a mess to deal with within the format specifier would be `"` and `}` and I would think that either or both could be escaped (via `\` in C# and doubling-up in VB.) Could you explain the reasoning that a format like "0.00" would present a difficulty? + +--- + +On Oct 9, 2014 at 9:27 PM @lwischik wrote: + +**Halo_Four wrote:** +> Oof! That seems _way_ overly restrictive. Why does it need to be so restrictive? Honestly the only characters that I think might be a mess to deal with within the format specifier would be `"` and `}` and I would think that either or both could be escaped (via `\` in C# and doubling-up in VB.) Could you explain the reasoning that a format like "0.00" would present a difficulty? + +0.00 wouldn't present difficulty itself. + +The general format is `\{ expr [,alignment] [:formatString] }`. Here are examples which would be awkward to parse: +``` +\{ a ? b : c } // no format specifier +\{ a ? b : c : d} // format specifier = "d" +\{ a ? b : c : d : e} // format specifier = "d:e" +\{ a ( b ?? c, d} // no format specifier and there's an error about missing closing parenthesis +\{ a ( b ?? c, d : e} // format specifier = ",d:e" and there's an error about missing closing parenthesis +``` +The comma is pretty bad. To get it correct you'd need to know that C# never allows comma except inside parentheses, and count up parentheses to make sure they're all closed. Also a single mistyped character would radically alter the hole+format from one meaning into another, so it'd be a goofy typing+colorizing experience. What's confusing is that _almost every valid C# expression is also a valid format specifier with radically different meaning_. + +Would it be possible to allow arbitrary format strings? Possibly. The way I'd start is by asking, "When we consume a : or , or }, can we determine without lookahead whether this token should be interpreted part of the currently-parsed expression or a format specifier or hole delimeter?" I'm sure this would require a stateful parser (which C# doesn't currently have). + + +--- + +On Oct 9, 2014 at 9:57 PM @Halo_Four wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > Oof! That seems _way_ overly restrictive. Why does it need to be so restrictive? Honestly the only characters that I think might be a mess to deal with within the format specifier would be `"` and `}` and I would think that either or both could be escaped (via `\` in C# and doubling-up in VB.) Could you explain the reasoning that a format like "0.00" would present a difficulty? +> +> 0.00 wouldn't present difficulty itself. +> +> The general format is `\{ expr [,alignment] [:formatString] }`. Here are examples which would be awkward to parse: +> ``` +> \{ a ? b : c } // no format specifier +> \{ a ? b : c : d} // format specifier = "d" +> \{ a ? b : c : d : e} // format specifier = "d:e" +> \{ a ( b ?? c, d} // no format specifier and there's an error about missing closing parenthesis +> \{ a ( b ?? c, d : e} // format specifier = ",d:e" and there's an error about missing closing parenthesis +> ``` +> The comma is pretty bad. To get it correct you'd need to know that C# never allows comma except inside parentheses, and count up parentheses to make sure they're all closed. Also a single mistyped character would radically alter the hole+format from one meaning into another, so it'd be a goofy typing+colorizing experience. What's confusing is that _almost every valid C# expression is also a valid format specifier with radically different meaning_. +> +> Would it be possible to allow arbitrary format strings? Possibly. The way I'd start is by asking, "When we consume a : or , or }, can we determine without lookahead whether this token should be interpreted part of the currently-parsed expression or a format specifier or hole delimeter?" I'm sure this would require a stateful parser (which C# doesn't currently have). + +Oh ok, you meant that the error was that the `.` in the format specifier wasn't being translated to a literal `.` but to whatever the decimal separator character would be. Gotcha. I agree with the parsing concerns and the C# ternary operator really throws things for a loop. In my opinion, I would have the parser aggressively attempt to match for the alignment or format specifier characters and only permit them when they are used unambiguously, such as within either parenthesis or brackets. If you wanted to use the ternary operator in string interpolation you'd be forced to do the following: + +```cs +int count = 2; +string s = "Hi, I have \{count} peach\{ (count == 1 ? string.Empty : \"es\" ) : 000 }!"; +``` + +I can't think of a scenario in which a comma would be valid syntax outside of either brackets or parenthesis but I might be missing something. + +Of course I can also understand wanting to start restrictive and to test the water before considering being more lenient. + +As for the locale invariance, what's the story on the prefix concept so far? At the simplest I could see it being some instance of `IFormatProvider` which is fed to the resulting `String.Format` call: + +```cs +float f = 123.45f; +IFormatProvider inv = CultureInfo.InvariantCulture; +string s = inv"The value is \{f}."; +``` + +Possibly to be extended later to include support for a second interface which could be capable of more intelligent handling of the tokens, like the tagged string template functions in EcmaScript 6. + +--- + +On Oct 10, 2014 at 1:32 PM @BachratyGergely wrote: + +**lwischik wrote:** +> The scenario the switched our minds was the typing process, e.g. I type `var x = "hello |` and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am. + +The primary concern should be the reading process. We're going to write the code only once (ideally) and read it an awful lot of times. For me that means that any time I have to do escaping I almost always do a verbatim string, especially when quotes and backslashes are present - which usually are for common data types such as JSON, XML or a regex. Having a `{Regex.Escape(someStuff)}` sounds like a common use case, however I'd have to double every backslash in the regex pattern. I'd much more prefer double-brace which is consistent with String.Format, less likely to appear in a regex and easier to read. +The `$` prefix would also be beneficial. Most of my arguments (like not having to mentally parse the string) has been said before, but let me just add some more: I would have to check the beginning of the string anyway because in the current proposal `"some long text \{count}"` and `@"some long text \{count}"` are obviously both legal but have very different semantics. One more reason is that an interpolated string is NOT a compile-time constant and very much context-dependent since it may use current values of local variables. Having a very explicit way to indicate it is a special string may help with feature discoverability and make the occassional intern think twice before moving the string e.g. to a resource dictionary. + +About the typing concern: I frequently use a keyboard layout where `\` is actually a key-combination. Even though `{` is also a combination it is much faster to repeat the same character twice than escaping with `\` then typing `{`. So having a "verbatim" interpolated string is easier to both read and type. + +**nmgafter wrote:** +> Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there. + +This is the exact reason why I always go for String.Format when the expression is not trivially short, even though it's slower. That way there's only a single string, no interruptions. + +--- + +On Oct 10, 2014 at 1:40 PM @Przemyslaw wrote: + +Just curious - is interpolated string still treated by compiler as constant primitive value? E.g. can I use interpolated string as value for string constant or as parameter to some attribute or as default value for method parameter? Is the answer always: +* yes - it's string after all, +* no - it gets rewritten as string.Format() which is not compile-time const value +* it depends - compiler is smart enough to handle situation where it uses only compile-time const values in its interpolated parts + +I expect "no", but "it depends" would also be nice - at least for cases when string or char constants are referenced in interpolated expressions. + +--- + +On Oct 10, 2014 at 1:49 PM @KathleenDollard wrote: + +Lucian and Halo_Four, + +The ternary operator scenarios with formatting is sufficiently difficult to read (two colons meaning two quite different things) that I would be quite happy if you required parenthesis on the ternary. I was going to suggest excluding ternary operators, but the parens made it non-impossible to mentally parse. + +The compiler should be allowed more complex rules than the humans can manage. In this case, the colons are a problem for the humans. + +Kathleen + +--- + +On Oct 10, 2014 at 1:54 PM @KathleenDollard wrote: + +**Przemyslaw wrote:** +> Just curious - is interpolated string still treated by compiler as constant primitive value? E.g. can I use interpolated string as value for string constant or as parameter to some attribute or as default value for method parameter? Is the answer always: +> * yes - it's string after all, +> * no - it gets rewritten as string.Format() which is not compile-time const value +> * it depends - compiler is smart enough to handle situation where it uses only compile-time const values in its interpolated parts +> +> I expect "no", but "it depends" would also be nice - at least for cases when string or char constants are referenced in interpolated expressions. + +Wouldn't the scenarios where this would be useful be very rare - only cases where you are inserting constants? + +If the compilers will be changing in their intelligence about what they can keep out of string.Format, then I vote for No. Even without that issue, I think I vote for No. At least in v1. Not a strong thing, just sounds scary. + +Kathleen + +--- + +On Oct 10, 2014 at 2:43 PM @Halo_Four wrote: + +**Przemyslaw wrote:** +> Just curious - is interpolated string still treated by compiler as constant primitive value? E.g. can I use interpolated string as value for string constant or as parameter to some attribute or as default value for method parameter? Is the answer always: +> * yes - it's string after all, +> * no - it gets rewritten as string.Format() which is not compile-time const value +> * it depends - compiler is smart enough to handle situation where it uses only compile-time const values in its interpolated parts +> +> I expect "no", but "it depends" would also be nice - at least for cases when string or char constants are referenced in interpolated expressions. + +Good question. Lucian did mention optimization scenarios where interpolation would use String.Concat if the values did not use any composite formatting, and String.Concat can be optimized to a literal if all of the concatenated values are constant or known at compile-time. I would be interesting if interpolation could take advantage of that existing optimization. + +The following might also be nice: + +```cs +public const string FOO = "foo"; +public const string BAR = "bar \{FOO} baz"; + +``` + +--- + +On Oct 10, 2014 at 2:51 PM @Halo_Four wrote: + +**KathleenDollard wrote:** +> Lucian and Halo_Four, +> +> The ternary operator scenarios with formatting is sufficiently difficult to read (two colons meaning two quite different things) that I would be quite happy if you required parenthesis on the ternary. I was going to suggest excluding ternary operators, but the parens made it non-impossible to mentally parse. +> +> The compiler should be allowed more complex rules than the humans can manage. In this case, the colons are a problem for the humans. +> +> Kathleen + +I don't disagree. I think that allowing for characters like `"`, `{`, `,` or `:` within the interpolation expression is a potentially dangerous rabbit hole both from the perspective of what the parser will have to deal with and what is reasonably readable to someone maintaining the code. I'm mentally pursuing it largely as an academic exercise but I wouldn't blame the Roslyn team for deciding to simply not support them, at least for the time being. + +That said, I do think that a common use-case might be the embedded pluralization of a term based on a variable, such as my example above. That hits both the ternary operator and embedded strings. Apple Swift can't do this. EcmaScript 6 can. + +--- + +On Oct 10, 2014 at 6:29 PM @lwischik wrote: + +**Halo_Four wrote:** +> The following might also be nice: `public const string FOO = "foo"; public const string BAR = "bar \{FOO} baz";` + +Again, no. The rule is very simple. Interpolated strings are equivalent in meaning to String.Format, end of story. That's a nice clean simple way to explain it. It implies they can never be constant. If there are optimizations under the hood (where the compiler has hard-coded knowledge of how String.Format would behave) then that's a good internal optimization, but it doesn't affect the language. + +I think it'd be bad if optimizations could change the constness of something. You might have an expression be "const" in C#7 where it was non-const in C#6, if the optimizer gets better. + +-- Lucian Wischik, VB/C# language team + +--- + +On Oct 10, 2014 at 6:37 PM @lwischik wrote: + +**Halo_Four wrote:** +> That said, I do think that a common use-case might be the embedded pluralization of a term based on a variable, such as my example above. That hits both the ternary operator and embedded strings. Apple Swift can't do this. EcmaScript 6 can. + +This code works in the current prototype: +```cs +var s = "you have \{i} lobster\{i==0?"" :"s"}"; +``` + +-- Lucian Wischik, VB/C# language team + +--- + +On Oct 10, 2014 at 7:16 PM @btjdev wrote: + +It's sounding like I might be alone on this, but just to check my understanding of where the proposal is headed, the following example makes me a little sad: + +```path = "\\\\\{server}\\\{share}\\\{subdir}\\\{file}.\{ext}";``` + +Using the proposed syntax to build a complex dynamic regex seems even more daunting. + +Something more like this next example would make me happy, as it seems like a nice readable solution for building both paths and especially regex expressions, and feels more like it fits with existing C# convention: + +```path = $@"\\{server}\{share}\{subdir}\{file}.{ext}";``` + +My conclusion - the string interpolation proposal is mostly being discussed in the context of simple user messages, and is aiming to achieve parity with other languages for that narrow case. To justify the investment, I was hoping for more. C# added @ partly because, unlike languages born on other OSs, backslash is more heavily used in Windows. The current proposal not only neglects that issue, it also adds yet another usage of backslash to the existing noise. + +--- + +On Oct 10, 2014 at 7:23 PM @tom103 wrote: + +**btjdev wrote:** +> It's sounding like I might be alone on this, but just to check my understanding of where the proposal is headed, the following example makes me a little sad: +> +> ```path = "\\\\\{server}\\\{share}\\\{subdir}\\\{file}.\{ext}";``` +> +> Using the proposed syntax to build a complex dynamic regex seems even more daunting. +> +> Something more like this next example would make me happy, as it seems like a nice readable solution for building both paths and especially regex expressions, and feels more like it fits with existing C# convention: +> +> ```path = $@"\\{server}\{share}\{subdir}\{file}.{ext}";``` +> +> My conclusion - the string interpolation proposal is mostly being discussed in the context of simple user messages, and is aiming to achieve parity with other languages for that narrow case. To justify the investment, I was hoping for more. C# added @ partly because, unlike languages born on other OSs, backslash is more heavily used in Windows. The current proposal not only neglects that issue, it also adds yet another usage of backslash to the existing noise. + +No, you're not alone, I don't like it either. Being able to combine verbatim strings and string interpolation with the $ prefix would be nice. On the other hand, a sequence of characters like "$@" is quite ugly... it's starting to make C# look like Perl + +--- + +On Oct 10, 2014 at 7:45 PM @Halo_Four wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > The following might also be nice: `public const string FOO = "foo"; public const string BAR = "bar \{FOO} baz";` +> +> Again, no. The rule is very simple. Interpolated strings are equivalent in meaning to String.Format, end of story. That's a nice clean simple way to explain it. It implies they can never be constant. If there are optimizations under the hood (where the compiler has hard-coded knowledge of how String.Format would behave) then that's a good internal optimization, but it doesn't affect the language. +> +> I think it'd be bad if optimizations could change the constness of something. You might have an expression be "const" in C#7 where it was non-const in C#6, if the optimizer gets better. +> +> -- Lucian Wischik, VB/C# language team + +Well, I would "argue" that there is precedent to the contrary given that `+` is supposed to be equivalent to calling `String.Concat` but is still permitted to concatenate literals or constants into another constant, but that is an explicitly specified exception. I definitely get your point, apart from requiring that the optimizations all be handled up front it creates confusion as to what rules may be permitted to construct an interpolated constant. And honestly if that's what you wanted, you can always just concat. + +--- + +On Oct 10, 2014 at 7:55 PM @Halo_Four wrote: + +**lwischik wrote:** +> **Halo_Four wrote:** +> > That said, I do think that a common use-case might be the embedded pluralization of a term based on a variable, such as my example above. That hits both the ternary operator and embedded strings. Apple Swift can't do this. EcmaScript 6 can. +> +> This code works in the current prototype: +> ```cs +> var s = "you have \{i} lobster\{i==0?"" :"s"}"; +> ``` +> +> -- Lucian Wischik, VB/C# language team + +Without a form of escaping/quoting? That is kind of weird. I'm trying to think of ambiguous edge cases where the interpolated string is the true-expression of another ternary operator but they'd probably all be syntax errors for one reason or another anyway. + +--- + +On Oct 10, 2014 at 10:42 PM @AdamSpeight2008 wrote: + +It can be a compile time constant if the referred to args are knowable at compile time (constants and literals). +If we are also to include the alignment and formatting parameter (of arg holes in String.Format), it still is possible. The awkward part is constructing the result efficiently as you need to know the alignment value, which could 1 million chars wide per an a arghole. (foi That magic number is hidden in string.format's implementation), also it could be either left or right aligned. Not all formatting string are value for all types. My diagnostic (String.Format Diagnostic) I had to reimplement the format parser, (cos the .net base class source code original are not available in the reference source), to give informative results. It not like you can plug in to existing parsers. + +--- + +On Oct 14, 2014 at 4:11 PM @supercat wrote: + +**Halo_Four wrote:** +> Without a form of escaping/quoting? That is kind of weird. I'm trying to think of ambiguous edge cases where the interpolated string is the true-expression of another ternary operator but they'd probably all be syntax errors for one reason or another anyway. + +I was just thinking about how some command-line text editors handled search parameters and wondering if the principle might be useful here. In particular, might it make sense to allow some form of tag between the `$` and the opening quote, and require that neither open-brace+tag+dollar-sign nor dollar-sign+tag+close-brace could appear in a formatting hole? Thus, one could have e.g. + + myString = $X"Hello there. {X$ someCondition ? $Y"Value is {Y$ someValue $Y}" : $Y"It was {Y$ someOtherValue $Y}" $X}"; + +including an interpolated string within a hole without creating ambiguity for either a parser or a human reader, since the ending delimiter for the interior formatting holes would be different from that of the outside ones. Further, one could copy and paste an interpolated string as a string literal within an interpolated string without having to escape the hole delimiters within it, if one chose a tag for the outer string which didn't appear anywhere within the inner one. + +--- + +On Oct 19, 2014 at 6:55 AM @r_keith_hill wrote: + +**btjdev wrote:** +> It's sounding like I might be alone on this, but just to check my understanding of where the proposal is headed, the following example makes me a little sad: +> +> ```path = "\\\\\{server}\\\{share}\\\{subdir}\\\{file}.\{ext}";``` +> +> Using the proposed syntax to build a complex dynamic regex seems even more daunting. +> +> Something more like this next example would make me happy, as it seems like a nice readable solution for building both paths and especially regex expressions, and feels more like it fits with existing C# convention: +> +> ```path = $@"\\{server}\{share}\{subdir}\{file}.{ext}";``` +> +> My conclusion - the string interpolation proposal is mostly being discussed in the context of simple user messages, and is aiming to achieve parity with other languages for that narrow case. To justify the investment, I was hoping for more. C# added @ partly because, unlike languages born on other OSs, backslash is more heavily used in Windows. The current proposal not only neglects that issue, it also adds yet another usage of backslash to the existing noise. + +You're not alone. I don't like it either. + +--- + +On Oct 19, 2014 at 2:29 PM @Halo_Four wrote: + +**r_keith_hill wrote:** +> You're not alone. I don't like it either. + +How about this? Allow both. For normal strings you'd use escaped holes. For verbatim strings you'd use a `$` prefix _instead of_ the current `@` prefix. + +```cs + +// equivalent (assuming I counted my slashes correctly) +string path1 = "\\\\\{server}\\\{share}\\\{subdir}\\\{file}.\{ext}"; +string path2 = $"\\{server}\{share}\{subdir}\{file}.{ext}"; + +``` + +This does add the requirement of escaping the hole syntax within the verbatim strings by doubling them up which would be the same as what people are currently used to doing for both composite format strings and double quotes within a verbatim string. + +It would be more work but I think it would diffuse the argument and allow interpolation within verbatim strings which I know some people were disappointed that it was off of the table. + +--- + +On Oct 19, 2014 at 2:32 PM @KathleenDollard wrote: + +I am a fan of the prefix as well. + +Keith, why do you want both the $ and the @ in your syntax? + +--- + +On Oct 19, 2014 at 8:13 PM @r_keith_hill wrote: + +**Halo_Four wrote:** +> **r_keith_hill wrote:** +> > You're not alone. I don't like it either. +> +> How about this? Allow both. For normal strings you'd use escaped holes. For verbatim strings you'd use a `**Halo_Four wrote:** + prefix _instead of_ the current `@` prefix. +> +> ```cs +> +> // equivalent (assuming I counted my slashes correctly) +> string path1 = "\\\\\{server}\\\{share}\\\{subdir}\\\{file}.\{ext}"; +> string path2 = $"\\{server}\{share}\{subdir}\{file}.{ext}"; +> +> ``` +> +> This does add the requirement of escaping the hole syntax within the verbatim strings by doubling them up which would be the same as what people are currently used to doing for both composite format strings and double quotes within a verbatim string. +> +> It would be more work but I think it would diffuse the argument and allow interpolation within verbatim strings which I know some people were disappointed that it was off of the table. + +You know, that's a very interesting suggestion. It addresses the issues building both paths AND regular expressions where you don't want \ to be an escape char. And most of the time I would just use $"" because most of the time I don't need to escape a char and frankly seeing multiple escaped holes just isn't as readable. Actually it's just plain fugly. So $"" would mean verbatim/interpolated combo string. I really like it! + +--- + +On Oct 19, 2014 at 8:25 PM @AdamSpeight2008 wrote: + +If formatting arg (of arg holes) is allowed \ is also used in custom format strings. + +--- + +On Oct 19, 2014 at 8:30 PM @r_keith_hill wrote: + +**KathleenDollard wrote:** +> I am a fan of the prefix as well. +> +> Keith, why do you want both the $ and the @ in your syntax? + +Did you mean you're *not* a fan? Well that was @btjdev that wanted both but I get the reason - constructing paths and regexes with interpolation. I actually like @Halo_four's suggestion about making $"" be a combo verbatim/interpolated string. I think the readability suffers with escaped holes and I'm not sure I buy that escaping is the "C" derived language way to go. Escaping in C langs outputs the escaped char. This is not the cases with escaped holes. + +--- + +On Oct 20, 2014 at 6:55 AM @madrian wrote: + +If the decision is to use the backslash maybe we can use the prefix for "only expand to argument list" solution. + +``` +var sb = new StringBuilder(); +sb.AppendFormat($"\{x} \{y}"); +string.Format(CultureInfo.InvariantCulture, $"\{x} \{y}"); +Trace.TraceError($"\{x} \{y}") +``` + + + +--- + +On Oct 20, 2014 at 1:23 PM @AdamSpeight2008 wrote: + +__@r_keith_hill__ How do we present the verbatim content ```"\{x} \{y}"``` in a interpolated string? + +--- + +On Oct 20, 2014 at 3:01 PM @Przemyslaw wrote: + +the "$" prefix has one more advantage. It can be easily extended to support not-string-literals, e.g: +``` +var foo = ... +var bar = ... +var message = $Resources.MyMessage; +``` +This would also work with custom interpolators: +``` +var foo = ... +var bar = ... +var message = invariant$Resources.MyMessage; +``` + +Readability of the above code is crippled comparing to: +``` +var foo = ... +var bar = ... +var message = invariant$"Foo is {foo} and bar is {bar}"; +``` +But it works better with i18n. Smart rename inside VS can help with updating things inside .resx files when one renames "foo" to something else. Also diagnostics can warn or even break build if string inside .resx contains references to elements not existing in scope. Of course smart rename and diagnostic can work with escaped \{...} holes too, but $ prefix gives at least some clue that content is interpolated and dev should be careful when facing such code outside VS. + +--- + +On Oct 20, 2014 at 3:04 PM @Halo_Four wrote: + +**Przemyslaw wrote:** +> the "$" prefix has one more advantage. It can be easily extended to support not-string-literals, e.g: +> ``` +> var foo = ... +> var bar = ... +> var message = $Resources.MyMessage; +> ``` +> This would also work with custom interpolators: +> ``` +> var foo = ... +> var bar = ... +> var message = invariant$Resources.MyMessage; +> ``` +> +> Readability of the above code is crippled comparing to: +> ``` +> var foo = ... +> var bar = ... +> var message = invariant$"Foo is {foo} and bar is {bar}"; +> ``` +> But it works better with i18n. Smart rename inside VS can help with updating things inside .resx files when one renames "foo" to something else. Also diagnostics can warn or even break build if string inside .resx contains references to elements not existing in scope. Of course smart rename and diagnostic can work with escaped \{...} holes too, but $ prefix gives at least some clue that content is interpolated and dev should be careful when facing such code outside VS. + +Interpolation being a compile-time feature that wouldn't work with string resources. + +--- + +On Oct 20, 2014 at 6:23 PM @AdamSpeight2008 wrote: + +If they are Application Setting you coukd since therey are readonly at runtime. + +--- + +On Oct 20, 2014 at 6:38 PM @supercat wrote: + +**r_keith_hill wrote:** +> You know, that's a very interesting suggestion. It addresses the issues building both paths AND regular expressions where you don't want \ to be an escape char. + +I wonder if it would be helpful to (for strings in general) define a new escape sequence `\!` which would expand to `\`? Something like `\!\!\!\n` would be much more recognizable as three backslashes and a newline than would be `\\\\\\\n`. Even though `\\` would need to be allowed for compatibility, I personally wish almost all uses of the double-character-to-escape-it pattern (not just in C#, but everywhere) could be eliminated. Unless the number of legitimate values for the second character of an escape sequence exceeds the number of legitimate values that wouldn't need escaping, the pattern has no real advantage over picking some other character, but can make it impossible to know the meaning of a character without examining an arbitrary number of characters before it. By contrast, in any string which doesn't happen to contain and old-style backslash escape, the largest number of characters one would have to read back would be two [if the previous character was a backslash, one would have to read the character before that to ensure it wasn't also a backslash]. + +--- + +On Oct 20, 2014 at 7:10 PM @Halo_Four wrote: + +**AdamSpeight2008 wrote:** +> If they are Application Setting you coukd since therey are readonly at runtime. + +Which isn't relevant since it's not known at compile time and the compiler would have no idea how to interpolate scoped expressions. Even if there were custom interpolators the syntax and holes would have to be resolved at compile time in order to provide the correct tokens. + +--- + +On Oct 20, 2014 at 11:50 PM @r_keith_hill wrote: + +**AdamSpeight2008 wrote:** +> __@r_keith_hill__ How do we present the verbatim content ```"\{x} \{y}"``` in a interpolated string? + +If $"" represented a verbatim and interpolated string then one option is that you don't. You would have to use a regular string with escaped holes. While that would probably be fine for constructing paths (curly braces are legal but uncommon), unfortunately curly braces are far more common in regular expressions. Dang, that blows a hole in this idea for regular expressions. :-( BTW it might be tempting to suggest using something like {{ }} or \\{ to escape curly braces in this $"" combo verbatim/interpolated string but I don't like the idea of using any form of character escaping in a "verbatim" string. + +--- + +On Oct 21, 2014 at 12:05 AM @nmgafter wrote: + +Please see https://roslyn.codeplex.com/discussions/570292 for a revised specification using the $"" syntax. That is our latest thinking based on feedback here. Lets move discussion to that thread please. + +--- + diff --git a/meetings/2014/LDM-2014-04-16.md b/meetings/2014/LDM-2014-04-16.md new file mode 100644 index 0000000..9dd47df --- /dev/null +++ b/meetings/2014/LDM-2014-04-16.md @@ -0,0 +1,87 @@ +# #Warning Disable + +## REQUEST + +Please add `#pragma` to VB so we can suppress warnings. This will be especially useful for Roslyn Diagnostics, where people will find many more warnings in their code. + +## RESOLUTION + +Add two new preprocessor directives: + +``` vb +#Disable Warning 40008, BC42358, "BC42356" +#Enable Warning 40008, BC42358, "BC42356" +``` + +## ALTERNATIVE SYNTAXES CONSIDERED + +``` vb +#pragma disable +#pragma enable + +#Disable ' sounds like you're disabling code, not warnings +#Enable + +#Ignore +#End Ignore ' block structure is worse than toggles + +#Disable Warning +#Enable Warning +``` + + +## DISCUSSION + +The tradition in VB and its rich history of quick-fixes is that you resolve warnings by FIXING YOUR CODE, e.g. by doing whatever the quickfix says, adding an explicit cast, adding a return statement, ... + +Q. Do people really use #pragma much in C# today? Hard to say. With new Roslyn diagnostics, will *more* people start using #pragma in C# ? Will people write Roslyn diagnostics that have a much higher false-positive rate than existing rules, for people who wish to be more careful, safe in the knowledge that it's easy to suppress? + +Q. Is there a downside to adding #pragma to VB for suppressing warnings? - no, not really + +Q. Should we add a feature which suppresses the warning merely for the next line, rather than for the rest of the file? -- This isn't feasible in C# where lines don't really exist semantically. It is feasible for VB. Disadvantage: would confusing for LINQ, and not refactoring-safe. Advantage: if the lightbulb/quickfix appears for a warning, and when you chose the option to "suppress warning" then it only has to insert a single line to "suppress-warning-on-next-line", rther than having to insert `#ignore` before and `#restore` after. Conclusion: No we won't add this next-line-suppression feature. + +Q. Is it good to disable/enable on a "block" basis like `#Ignore / #End Ignore` ? Or on a toggle basis like `#Disable Warning / #Enable Warning` ? Well, with blocks you can't do overlapping regions. And you can't easily disable warnings for an entire method but then re-enable them for a small critical region inside (even though that's a weird scenario). We believe overall that toggles are nicer. They have a lot of engineering, thought and experience behind them. + +Q. For the ``, should quotation marks be obligatory? - No. You can use any identifier or integer. It's only if the warning-id has spaces or other weird characters that you'd need quotation marks. + +Q. Should we allow pure integers for the ``, with the expectation that they disable intrinsic VB compiler warnings? All user-defined warnings (that come through Roslyn diagnostics) would have error codes that aren't pure integers. -- Yes, let's allow integers, just like C#, and just like the VB command-line. It will be a common case. + +Q. Will the Roslyn diagnostics APIs allow people to define error codes that are just pure numbers, even though they'll be impossible to disable them? -- No real need. Diagnostic-authors will learn soon enough not to do this. + +Q. Should we allow you to disable/enable command-separated lists of warnings in a single `#Disable Warning / #Enable Warning` directive? -- Yes. + +Q. Preprocessor directives look ugly because they're always left-aligned. Can we make it so preprocessor directives can be indented? There's a uservoice request for it. -- Not discussed; left for another day. + + +# Back-compat break with interface implementations + +We also discussed another topic to do with ambiguity between unrelated interfaces. Discussion is in this thread: +https://roslyn.codeplex.com/discussions/570975 + + +# Update: VB LDM 2014.07.01 + +We re-visited #Disable Warning. The summary for VB is: + +* Use the syntax `#Disable Warning id1, id2, ...` +* The warning IDs must parse as per the same rules of VB identifiers, and are case-insensitive. That rules out plain numbers `#Disable Warning 4008` (instead you must disable BC4008) and it rules out strings `#Disable Warning "Don't Use Capitals!"`. That means you can't disable warnings that have fancy error codes with punctuation and so on. +* This will guide users to the thought that they can right-click, find all references, and opens up the mental door to other refactorings/tooling +* We will restrict what identifiers can be produced at the production side too, when authoring analyzers +* We used to give BC2026 “that warning can’t be configured” when you try to nowarn or warnaserror on non-existent warnings (and also on certain named warnings e.g. 2026). But that wouldn’t work for diagnostic warning codes. So let’s no longer emit BC2026 ever. Instead, if you try to disable or enable a non-existing warning, simply do nothing. +* Stretch goal: the IDE could grey out all identifiers that have been disabled but are never actually generated by the code + +C# will be mostly similar, but +* C# will support disabling warnings/errors by number as well as by identifier, and is case sensitive. It has to support disabling numbers for back-compat. If you disable warning number 4008, that's equivalent to disabling identifier CS4008. +* As with VB, we stop validating whether the warning code is a legal compiler warning ID (both on the command-line and inside #pragma) - CS1691 will no longer be reported for C#. + +Things that fall out for free: +* No changes required to disallow Hexadecimal, Octal, Long etc. literals +* No need to worry about making BC2026 unerrorable + +For the future: +* Update lightbulb suppression to always generate #pragma / #disable with identifiers. (Do we need to check that the id will be a legal identifier before spitting the code?) +* Fix /nowarn and /warnaserror command-line switches to support custom diagnostics +* Implement grey out for “unused” ids in #pragma, #Disable (plus corresponding code fix) +* Have some way for users to get from a reported warning id / from a warning id used in #pragma or #Disable to a description of the diagnostic +* Support for qualified identifiers that use dots (only if customers ask for it) + diff --git a/meetings/2014/LDM-2014-04-23.md b/meetings/2014/LDM-2014-04-23.md new file mode 100644 index 0000000..ef9db8d --- /dev/null +++ b/meetings/2014/LDM-2014-04-23.md @@ -0,0 +1,957 @@ +(this discussion happened in VB language design meeting, but it strongly concerns C# also). + + +# Better support for immutable data, with pattern-matching + +## Request + +Please make it easier for me to declare immutable data-types, and make it easier to code over them via pattern-matching. Easier immutable datatypes will make my code have fewer defects, and pattern-matching over immutable data (like in F#, Scala and other functional languages) will allow clean readable maintanable coding idioms. + +For instance, I have a method and want to return 2-3 return values. Here's the ugly code I write now: +``` cs +// Ugly 1: use out params (but doesn't work with async) +void Lookup(out string name, out int x, out int y) {...} +... +Lookup(out name, out x, out y); Console.WriteLine(name); + + +// Ugly 2: use a tuple (but my consumer has a hard time knowing which element is what) +Tuple Lookup() {...} +... +var r = Lookup(); Console.WriteLine(r.Item1); + + +// Ugly 3: declare a custom datatype +class LookupResult(string name, int x, int y) +{ + public string Name {get;} = name; + public int X {get;} = x; + public int Y {get;} = y; +} +LookupResult Lookup() {...} +... +var r = Lookup(); Console.WriteLine(r.Name); +``` + +What I'd like is to write all this much more simply: +``` cs +class LookupResult(string name, int x, int y); +LookupResult Lookup() {...} + +var r = Lookup(); Console.WriteLine(r?.name); +``` + +Even better, I'd like to deconstruct the returned value using pattern-matching: +``` cs +if (Lookup() matches LookupResult(var name, var x, var y)) { ... } +``` + + +## Background + +We have been guided in our thoughts by the paper "Matching Objects with Patterns" by Emir, Odersky and Williams. There's a link to it on this Scala webpage: http://www.scala-lang.org/old/node/112 + +The Roslyn compiler currently has "primary constructors" in C#, and a form of "typecase" in VB: + +``` cs +class C(int x, int y) +{ + public int X { get; set; } = x; + public int Y { get; set; } = y; +} +``` +``` vb +Function GetArea(shape As Shape) As Double + Select Case shape + Case r As Rect : Return r.X * r.Y + Case c As Circle : Return Math.PI * c.Radius * c.Radius + End Select +End Function + +' Once we add Out expressions to VB, you can get more pattern-matchey +Function Eval(expr As Expr) As Integer + Select Case expr + Case e As PlusExpr When e.Matches(0, Out rhs) + Return Eval(rhs) + Case e As PlusExpr When e.Matches(Out rhs, 0) + Return Eval(rhs) + Case e As PlusExpr When e.Matches(Out lhs, Out rhs) + Return Eval(lhs) + Eval(rhs) + Case e As IntExpr + Return e.Val + End Select +End Function +``` + +**Question: how will the two features work together?** Will there be a "sweet spot" where their designs mesh together well, and mesh with additional features in future versions of the languages, to produce a nice pattern-matching design? Something as nice as what F# or Scala have? + +**Urgent question: do we need to make changes to the CURRENT design of primary constructors and Select Case** so that we don't block off a future world of pattern-matching? + + +## PROPOSAL: Records, and Plain Old CLR Objects + +Here is a coherent set of proposals that combine construction and deconstruction into one neat easily-written proposal, to support the scenario in the "Request". + + +Imagine something called a *"Record"*, a type with an ordered list of named data members. Not saying there should be a new kind of type called a "record type" alongside classes and structs... indeed it might be best not to have a new kind of type, since we might want record-like classes and structs. + +**PROPOSAL 1:** a record can be defined using primary-constructor syntax, and it is syntactic sugar for an expanded form as follows... +``` cs +class Point(int X, int Y); + +==> + +class Point(int X, int Y) +{ + public int X { get; } = X; + public int Y { get; } = Y; +} +``` + +The rule is: "When you write a record, it automatically generates properties for the *PRIMARY PROPERTIES*, unless you have provided those properties yourself". The term "Primary Properties" refers to the parameters in the primary-constructor syntax. Thus, if you didn't want it to auto-generate a property, you'd have to provide your own version of that property, as in the example below. (There's no way to say that you *don't* want the property with this syntax: if you don't want the property, then don't use the feature at all). +```cs +class Point(int X, int Y) { public int X => 15; } + +==> + +class Point(int X, int Y) +{ + public int X => 15; + public int Y {get; set;} = Y; +} +``` + +**PROPOSAL 2:** Just as we've seen the compiler auto-generate properties if you failed to provide them yourself, it will also auto-generate a default implementation of Equals and GetHashCode if you fail to provide them yourself. This default implementation will do element-wise calls to Equals and GetHashCode on the primary properties. + +(By default, Proposal 1 uses immutable properties, and so Equals/GetHashCode will terminate. It's bad practice to for GetHashCode to calculate based on mutable properties.) + + +**PROPOSAL 3:** In addition to auto-generated properties, Equals, GetHashCode, we also auto-generate a MAtch operator (unless one is provided by the user)... + +``` cs +class Point(int X, int Y) +{ + public int X {get;} = X; + public int Y {get;} = Y; + public static bool operator Matches(Point self, out int X, out int Y) {X=self.X; Y=self.Y; return true;} +} +``` + +In addition to this, we introduce a new *MATCH* operator into the language: + +``` cs + matches T(args) +``` +Operational semantics: + +1. Evaluate "var tmp1 = " +2. Look in type "T" for an overload of "Matches" whose 2nd...last parameters match the arguments +3. Evaluate "var tmp2 = tmp1 As TFirst" where TFirst is the type of the first parameter of that overload +4. If this is null, then the match operator returns false +5. Otherwise, the result invoke the Match operator, passing it (tmp2, args) as arguments. + +You'd use the match operator like this: +``` cs +if (f() matches T(var name, var x, var y)) { ... } +``` +Note that this would have different rules from the way out vars are currently conceived. These out be definitely assigned only if the "e matches T(args)" returns true. Incidentally, note that the T's Match operator is not necessarily invoked... e.g. if f() returns null, then it won't be called! + + + +**PROPOSAL 4:** You could also treat the typename as a method, e.g. +``` cs +var p = Point(2,3); +==> +var p = new Point(2,3); +``` +All this does is remove the need for the word "new" for these record types. It might seem pointless, but it unifies the way you write construction and deconstruction... +``` cs +var p = Point(2,3); +if (p matches Point(out x, out y)) { ... } +``` + + +## Discussion + +There were questions raised, heated discussions, and no agreed-upon answers. + + +Q. If we go with primary constructors as they're currently envisaged (where the primary parameters *don't* define fields, and instead only exist at construction-time), then will we still be able to support this vision of pattern-matching? + +A. Not directly. But we could support this vision through a new modifier "record", e.g. `record class Point(int x, int y)` + + + +Q. What is the difference between putting all this "record" stuff (auto-generated properties, Equals, GetHashCode, Match operator) into the existing primary constructor syntax, vs putting them in via a new modifier "record" ? + +A1. It's a language-cleanliness problem. It would feel goofy for the primary constructor syntax to mean such different things depending on whether the "record" modifier is present. + +A2. Case is different. The convention in C# will be to use lower-case for primary constructor parameters, since users will write out the properties manually and use upper-case for that. But with records, the convention will probably be to use uper-case for the primary properties. + + + +Q. It feels wrong for primary-constructors to be so closely tied to immutable data and pattern-matching. Primary constructors have many other uses even when you don't want the rest, e.g. for coding visitor-patterns. Can we add all the features (primary constructors, Matches, Equals/GetHashCode, auto-generated properties) individually and orthogonally over time? And then use "concise declaration" (via whatever syntax) as the thread that ties everything together and delivers the smooth end-to-end use-case? + +A. Maybe our goal should be to make a concise story for the desired use-case, rather than to add orthogonal features? + + +Q. Is it important that users can declare Match operators today, right from the first version of primary constructors? (granted, this would be pretty much useless on its own: the Match operator only becomes useful in conjunction with all the other features). + + + +Q. How would these "record types" work with serialization? People commonly use Plain Old CLR Objects (POCO) when they want DataContract serialization. But there's no clear way with this proposal to attribute the individual properties. + + + +Q. What's needed to ship in the first version of primary-constructors so as to allow this kind of evolution over time? Are there any conflicts with the current proposals (of primary-constructors and SelectCase) that would prevent such evolution? + +A. Neal and Mads will meet, discuss this question, and take it to C# LDM next week. + + +--- +--- + +On Apr 30, 2014 at 6:34 AM @kekekeks wrote: + +Any chance of something like +``` +var res = match(data) +{ + matches T(var name, var x, var y)) => x + y +} +``` +? + +It would be great if that construction could match against types, lists, etc. I like the way it is implemented in Nemerle: https://github.com/rsdn/nemerle/wiki/Quick-guide#Pattern_Matching + +--- + +On May 2, 2014 at 3:57 AM @MgSam wrote: + +I mentioned some of this in the language notes thread; but I think it's worthwhile responding to everything in the proposals: + +Proposal 1 & 2: I like these a lot. I think they make it just as easy to declare useful named types as it is to declare anonymous types currently. Having `class Foo(int a);` syntax in the language would mean its so trivially easy to make DTOs that the horrid Tuple class would get much less use. I think these types should be able to be declared in method scope as well, just like anonymous types can be. Compiler generated GetHashCode, Equals, and ToString would make these sorts of types incredibly useful. I think having a separate `record` keyword is unnecessary however. + +3 & 4: With declaration expressions, this seems totally unnecessary. I can very easy write this myself: + +``` C# +public class Foo(public int A, public int B) +{ + public void Decompose(out int a, out int b) + { + a = A; + b = B; + } +} + +var foo = new Foo(3, 4); +foo.Decompose(out var a, out var b); +Console.WriteLine(a); +Console.WriteLine(b); +``` +Am I missing something? + +--- + +On May 2, 2014 at 5:04 AM @nmgafter wrote: + +**MgSam wrote:** +> 3 & 4: With declaration expressions, this seems totally unnecessary. I can very easy write this myself: +> +> Am I missing something? + +Yes, among other things you've missed a null check and you've taken the code out of an expression context. In your formulation pattern matching does not compose. This note does not describe my full proposal, but the full proposal would allow you to write + +``` C# +var foo = Foo(3, 4); +if (foo matches Foo(3, 4)) { // code to execute if this is a Foo with a=3 and b=4. +object o = foo; +switch (o) +{ + case Foo(3, var b): + // code to execute if o is a Foo with a=3, with b available as a variable + break; +} +``` + +See [http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf](http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf) for some of the inspiration behind this feature. + +--- + +On May 2, 2014 at 11:55 AM @ghordynski wrote: + +First of all, I really love the concept of pattern matching in C#. Some of my thoughts: + +1) Concerning the ```record``` keyword +> +The rule is: "When you write a record, it automatically generates properties for the PRIMARY PROPERTIES, unless you have provided those properties yourself" + +Why would you want to provide the properties yourself? Wouldn't it just be cleaner to only allow the syntax in the form: ```class Point(int X, int Y);``` ? It would eliminate the need for ```record``` keyword, as you could simply say that when class/struct has no body it is record (auto generated GetHashCode/Equals etc.). If you decide to allow those properties yourself, would they be used in GetHashCode/Equals auto generated methods? If so, what if someone decides to have them change each time they are used (for example returning random value from one of them). I think it would only allow subtle bugs and it's not really needed. + +2) The ```_``` parameter + +In many languages which have pattern matching, there appears to be identifier which matches every value and can be repeated any number of times in pattern matching clause without capturing the variable. Maybe it should be introduced here as well? + +``` + case Foo(3, _, _): + // code to execute if o is a Foo with a=3, with 2nd and 3rd parameter of foo ignored + break; +``` +I think it looks a little out of place in C#, so I would suggest some other form like keyword ```any```: +``` + case Foo(3, any, any): + // code to execute if o is a Foo with a=3, with 2nd and 3rd parameter of foo ignored + break; +``` + +I often wanted something similar in lambda expressions also: + +```item.Changed += (any, any) => ...``` + + +--- + +On May 2, 2014 at 1:52 PM @mirhagk wrote: + +First of all, the record feature should be implemented before Primary Constuctors are. The implementations of Primary Constructors has been debated by many people, and the team themselves have changed their mind about how to implement them. Primary Constructors should be delayed until something can really be figured out. + +These record types (by which I mean just the basic `Point(int X, int Y);` with auto-implemented ToString, Equals/GetHashCode) should be implemented first. I've yet to hear any complaints about these, they are a feature that's much more useful. + +Everyone you show that to goes "Oh that's awesome, when do we get it?". It should really be a priority for the team. (I'd be willing to look into it myself even). + +--- + +On May 2, 2014 at 6:26 PM @nmgafter wrote: + +**ghordynski wrote:** +> First of all, I really love the concept of pattern matching in C#. Some of my thoughts: +> +> 1) Concerning the ```record``` keyword +> > +> The rule is: "When you write a record, it automatically generates properties for the PRIMARY PROPERTIES, unless you have provided those properties yourself" +> +> Why would you want to provide the properties yourself? + +There are a number of reasons you might want to modify the default implementation. You might want to change its access level, or make it virtual or override. + +> 2) The ```_``` parameter +> +> In many languages which have pattern matching, there appears to be identifier which matches every value and can be repeated any number of times in pattern matching clause without capturing the variable. Maybe it should be introduced here as well? + +That is part of the full proposal (that you haven't seen). However, the syntax I proposed for a "wildcard" pattern is * + +--- + +On May 2, 2014 at 8:37 PM @supercat wrote: + +**mirhagk wrote:** +> These record types (by which I mean just the basic `Point(int X, int Y);` with auto-implemented ToString, Equals/GetHashCode) should be implemented first. I've yet to hear any complaints about these, they are a feature that's much more useful. + +The features have different kind of usefulness. Handy record declarations would reduce the amount of boilerplate code in many situations, but would not allow a programmer to do anything which couldn't already be done with a utility that took a list of members and auto-generated a boilerplate type definition. Primary constructors, if properly implemented, would make it possible to write classes which do things that are at present simply not possible in C#--at least not without horrible kludges involving ThreadStatic variables. + +That having been said, I think a substantial argument in favor of pushing handy aggregates ahead of primary constructors is that anyone whose needs aren't fulfilled by any aspect of a handy record declaration could simply implement types the old fashioned way unless or until a future version of C# adds options to better fit their needs. By contrast, if primary constructors lack a feature that someone needs, it's much less likely that there would be a workaround. Further, it may not be possible for future versions of C# to correct the deficiencies without breaking by-then-existing code. + +--- + +On May 15, 2014 at 7:53 PM @MgSam wrote: + +@C-SharpTeam Has there been any further discussion over any of the features discussed here? The proposed record types and syntax are easily the number 1 most useful thing that has been proposed for C# 6.0, at least in my workflow. More so than even primary constructors. + +For example, today I just wrote a class like this: + +``` C# + /// + /// A key for a metric. + /// + public struct MetricKey + { + /// + /// The symbol. + /// + public String Symbol { get; private set; } + /// + /// The date of the metric. + /// + public DateTime Date { get; private set; } + + /// + /// Creates a metric. + /// + /// Symbol. + /// The date. + public MetricKey(String symbol, DateTime date) : this() + { + Symbol = symbol; + Date = date; + } + + /// + /// Compares to another MetricKey for equality. + /// + /// The other MetricKey to compare to. + /// Whether the two MetricKeys were equivalent. + public bool Equals(MetricKey other) + { + return string.Equals(Symbol, other.Symbol) && Date.Equals(other.Date); + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// + /// true if and this instance are the same type and represent the same value; otherwise, false. + /// + /// Another object to compare to. 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is MetricKey && Equals((MetricKey) obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return ((Symbol != null ? Symbol.GetHashCode() : 0) * 397) ^ Date.GetHashCode(); + } + } + } +``` +The entire purpose of this class is to allow using a composite key with meaningful property names in a `Dictionary`. It would be amazing if I could write this whole thing as: + +``` C# +struct MetricKey(String Symbol, String Date); +``` + +That would be an enormous win. + +@nmgafter Thanks for the link. I understand the feature better now, though I still am not sure if its worth the cost of the added complexity to the language. A really compelling before-after use case might be helpful for people to better judge. + +--- + +On May 15, 2014 at 10:45 PM @supercat wrote: + +**MgSam wrote:** +> struct MetricKey(String Symbol, String Date); +> That would be an enormous win. + +Not sure how to resolve questions of whether the created type should have exposed unrestricted fields, exposed read-only fields, exposed read-only properties, or exposed read-write properties. If one is defining a structure type to serve as an aggregation of related-but-independent fields, an exposed-field structure is a perfect fit, and syntax like that quoted would very nicely indicate that the purpose of the struct would be to aggregate related-but-independent fields. Unfortunately, some people think that all types should behave like reference types, and code should never take advantage of situations where the semantics of aggregate types would better fit the task at hand. + +--- + +On May 15, 2014 at 11:44 PM @Olmo wrote: + +I think the ability to deconstruct entities will be hard to make in C#. I'll be happy with a more mundane solution that focuses on matching types, with optional conditions and returning values. + +Something like: + +```C# +var area = match shape + when (Rectangle r) then r.Width * r.Heigth + when (Circle c) then c.Radius * c.Radius * Math.Pi + when (Triangle t) and (t.IsRectangle || r.IsEqilateral) then t.Base * r.Height / 2 + default throw; +``` + +__Note:__ Polymorphism only works when you are in control of the class, not for classes on libraries. + +Maybe something along the lines can be done with the new conditional expressions: + +```C# +var area = (var r = shape as Rectangle) != null ? r.Width * r.Heigth : + (var c = shape as Circle) != c.Radius * c.Radius * Math.Pi : + (var t = shape as Triangle) && (t.IsRectangle || r.IsEqilateral) ? t.Base * r.Height / 2 + default throw; +``` + +The problem with this alternative is that the declarations will be available in the following statements, while with the match each variable is only visible in his clause. + +Also, in which state will be the variable `t` when `shape` is a `Rectangle`? Uninitialized? Will the declaration expression be so expressive? + + + +--- + +On May 16, 2014 at 6:32 AM @bradphelan wrote: + +If you are going to support immutable records then you need a sensible way of creating new versions. For example in imagined c# 6.0 + + record Skill(string Name, int Proficiency); + record Person ( string Name, Skill Skill); + + var me = new Person { Name = "Brad", Skill = new Skill { Name = "Programming", Proficiency = 5 }; + +So how can we update __me.Skill.Proficiency from __5__ to __7__ , given that me is immutable. Without any language support we would have to do + + + me = new Person { Name = me.Name, Skill = new Skill { Name = me.Skill.Name, Proficiency = __7__ }; + +which is ugly and prone to errors. What we need are a concept I understand as lenses. I've implemented this for my own projects as a reflection based solution. I would write the above code something like this. + + me = me.Set(p => p.Skill.Proficiency, 7); + +Note that this is better than the f# __with__ keyword because I can do the update to any level in the nested structure. I can't quite imagine what notation would make the above make sense. Here is an attempt. + + me = me..Skill.Proficiency := 7 + +We can break it down into two parts. Note the double _.._ this generates a __lens__ onto a root object. So that + + Lens lens = me..Skill.Proficiency; + +and + + Person meUpated = lens := 7; + +So a Lens is just a way to generate new nested records by specifying a sub node of the original nested record to focus on. It makes working with immutable types almost as easy as working with mutable ones. + +Anway it's just an idea. I do this via reflection in most of my code and it turns out pretty nice. + +--- + +On May 16, 2014 at 6:39 AM @supercat wrote: + +Although some people dislike them because they don't behave like objects, a nice approach to creating simple immutable objects is to encapsulate data within exposed-field structures. Doing that makes it very easy to build a new structure whose contents are identical to the original except for some specific changes, and then assign the structure to a `readonly` field, thus making all its fields immutable. Too bad the people behind .NET would rather complain that aggregate types don't behave like objects, than fix some of the limitations in .NET's handling of them. + +--- + +On May 19, 2014 at 7:13 PM @nmgafter wrote: + +**MgSam wrote:** +> @C-SharpTeam Has there been any further discussion over any of the features discussed here? The proposed record types and syntax are easily the number 1 most useful thing that has been proposed for C# 6.0, at least in my workflow. More so than even primary constructors. + +Records are definitely not going in to the next version of the language. However, I hope that we will be launching an open-source "experimental" fork of Roslyn, perhaps as soon as this summer, in which we start adding these proposed features to C# so we can evaluate them more fully for some future language version. + +--- + +On May 19, 2014 at 7:18 PM @MgSam wrote: + +An experimental fork sounds like a great idea. + +Is the feature set for C# 6.0 tweak-or-remove-only at this point? I assume the idea is to ship C# 6.0 with VS vNext. I just hope C# 7.0 won't be another subsequent 2, 3 year wait. + +--- + +On May 20, 2014 at 2:36 PM @KrisVDM wrote: + +Please reconsider adding the simplest record classes to C# 6. After all, how is `class Point(int X, int Y);` different from existing anonymous types, except that it's not anonymous? And do allow `struct Point(int X, int Y);` too, mutatis mutandis. + +In fact, being semantically different from anonymous types (except for being named and instantiated with constructor syntax) would be a strong argument against them. + +One could even argue that any additional feature you add later, e.g. the pattern matching, could be and maybe should be added to anonymous types as well. + +--- + +On May 20, 2014 at 9:43 PM @nmgafter wrote: + +**MgSam wrote:** +> Is the feature set for C# 6.0 tweak-or-remove-only at this point? I assume the idea is to ship C# 6.0 with VS vNext. I just hope C# 7.0 won't be another subsequent 2, 3 year wait. + +No, there are a number of language features that we still expect to implement for 6.0. + +We are currently having discussions about whether language changes can be dribbled out over minor releases, or whether they need to be bundled into larger sets less frequently. + +--- + +On May 21, 2014 at 1:55 PM @MgSam wrote: + +@nmgafter This is probably obvious from my previous post, but I vote strongly in favor of small releases that come out as the features are implemented, tested, and baked. Incredibly, CPlusPlus* has largely adopted this model, whereby the compiler vendors are implementing features as fast as the standards committee can ratify them. I think the response in the CPlusPlus community has been very positive to this new pace of innovation, and C#/VB/TypeScript would benefit from similar release cycles. If a standards committee from people across the globe can innovate a language as complex as CPlusPlus and quickly deliver positive results, surely the MS language design teams can as well. + +*Codeplex is HTML escaping the plus signs so I wrote out the name without them. + +--- + +On May 22, 2014 at 3:51 PM @Halo_Four wrote: + +**MgSam wrote:** +> @nmgafter This is probably obvious from my previous post, but I vote strongly in favor of small releases that come out as the features are implemented, tested, and baked. Incredibly, CPlusPlus* has largely adopted this model, whereby the compiler vendors are implementing features as fast as the standards committee can ratify them. I think the response in the CPlusPlus community has been very positive to this new pace of innovation, and C#/VB/TypeScript would benefit from similar release cycles. If a standards committee from people across the globe can innovate a language as complex as CPlusPlus and quickly deliver positive results, surely the MS language design teams can as well. +> +> *Codeplex is HTML escaping the plus signs so I wrote out the name without them. + +Probably not a good comparison as CPlusPlus technically evolves in much slower but larger chunks and it just takes the compiler vendors that long to dribble out the changes to bring their implementations into compliance. There have only been three standards for CPlusPlus published, initially in 1998 then in 2003 and finally in 2011, and the next isn't expected until 2017 (and will most likely be late). + +--- + +On May 22, 2014 at 3:59 PM @MgSam wrote: + +@Halo_Four Maybe you should look at this page: https://isocpp.org/std/status. You skipped CPlusPlus 2014. My implication was also that this is a model CPlusPlus has adopted _recently_, so pointing out the span of time between 98', 03', and 11' is not really relevant. + +Not to mention, for some of those CPlusPlus '14 features Microsoft and other compilers have already had them implemented and shipped for one or more years. + +--- + +On May 22, 2014 at 6:30 PM @Halo_Four wrote: + +**MgSam wrote:** +> @Halo_Four Maybe you should look at this page: https://isocpp.org/std/status. You skipped CPlusPlus 2014. My implication was also that this is a model CPlusPlus has adopted _recently_, so pointing out the span of time between 98', 03', and 11' is not really relevant. +> +> Not to mention, for some of those CPlusPlus '14 features Microsoft and other compilers have already had them implemented and shipped for one or more years. + +True, but a lot of that work is also library and not language where a concurrent approach is definitely better. But even so is there a single compiler that is actually 100% Cxx11 compliant? Visual Cxx 2013 certainly isn't, and it seems that there are minor things that GCC and clang both also don't support. As for experimental implementation of futures, that scares the crap out of me. Do we really want our programming languages to be as volatile as the web has become? At least with C# there has been pretty much one gatekeeper which has interfaced with the standards body. I'd hate for the C# development process to devolve into whatever the W3C or JCP has become where it seems that they are much more concerned with arguing over esotericism than solving real-world problems. + +--- + +On Jun 3, 2014 at 3:21 PM @MgSam wrote: + +As some food for thought for a future pattern matching feature, I thought it was interesting what Apple is doing with Swift and it's [switch statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html). + +--- + +On Jun 3, 2014 at 5:50 PM @AdamSpeight2008 wrote: + +VB.net can do all that already especially with the preview. + +--- + +On Jun 3, 2014 at 6:20 PM @MgSam wrote: + +**AdamSpeight2008 wrote:** +> VB.net can do all that already especially with the preview. + +The syntax is more similar to C# though (being of C lineage). Thus, I thought it interesting to take a look at. + +--- + +On Jun 10, 2014 at 6:36 PM @AdamSpeight2008 wrote: + +Will the pattern-matching be able to do the following? + +```vb +Select Case ( node.Left , node.Right ) + Case ( Integer, Integer ) + Case ( Double, Double ) + Case Else +End Select +``` +Long-hand version +```vb +If (TypeOf node.Left Is Integer) AndAlso (TypeOf node.Right Is Integer) Then + +ElseIf (TypeOf node.Left Is Double) AndAlso (TypeOf node.Right Is Double) Then + +Else + +End If +``` + +--- + +On Jun 11, 2014 at 2:14 AM @nmgafter wrote: + +**AdamSpeight2008 wrote:** +> Will the pattern-matching be able to do the following? +> +> ```vb +> Select Case ( node.Left , node.Right ) +> Case ( Integer, Integer ) +> Case ( Double, Double ) +> Case Else +> End Select +> ``` +> Long-hand version +> ```vb +> If (TypeOf node.Left Is Integer) AndAlso (TypeOf node.Right Is Integer) Then +> +> ElseIf (TypeOf node.Left Is Double) AndAlso (TypeOf node.Right Is Double) Then +> +> Else +> +> End If +> ``` + +We are working on matching with tuples as part of this, but it is unlikely we'd end up with precisely the syntax you proposed. In part because your cases did not introduce any variables. + +--- + +On Jun 11, 2014 at 3:46 AM @AdamSpeight2008 wrote: + +@nmgafter + +Introducing a new variable should be optional in my opinion, otherwise it's going start getting messy. +In the following example the variables are introduced via ``With { }`` parameter. + +```vb +Case Select (node.Left, node.Right ) + Case (Integer,Integer ) When ( some_x = 1000) With {a,b} : ' /* a::= node.Left b::= node.Right */ + Case (Integer,Integer ) When ( some_x > 1000) With { ,b} : ' /* b::= node.Right */ +``` +Make is easier to both express (don't care) and introduce variables. + +In long hand. +```vb +If( (TypeOf node.Left Is Integer ) AndAlso (TypeOf node.Right Is Integer) ) AndAlso + ( some_X = 1000) ) Then + ' Create variables for contents + Dim a = node.Left + Dim b = node.Right + ' a and b are now lexically scoped to be here + ... +Else If( (TypeOf node.Left Is Integer ) AndAlso (TypeOf node.Right Is Integer) ) AndAlso + ( some_X > 1000) ) Then + ' Create variables for contents + Dim b = node.Right + ' b is now lexically scoped to be here, and also is a different b to the one in previous [if clause] + ... +... + +``` +I think it is useful to consider what would the equivalent ```If ... Then ... Else If ... Else ... End If``` would be, + +Also only allow one of these types of case select clause per a clause and on its own. + +Otherwise you could have the situation where different variables are introduce depending on the clause. +```vb +Case (Integer,Integer) With New {a,b} , (Double, Double) With New {x,y} : + ' Error +``` +The you would then have the issue of how to express fall-through the following clause? + +```vb.net +Select Case Obj +Case (Integer) : GoTo Case caseA ' The GoTo Case scopes the GoTo be only in a following case clause. +Case (Double) : GoTo Case caseA ' Remove the Case and it becomes an ordinary GoTo +Case (Single) : GoTo Case caseA ' Trying to GoTo a clause beyond the next labelled case clause is an error, +Case (String) +caseA: + doStuff +``` + +Some would see that as "GoTo WTF!" , but I'd think of it a straight-jacketed insanity. + + +--- + +On Sep 4, 2014 at 12:01 PM @AdamSpeight2008 wrote: + +I've been think that the new constructs of __As__ and __When__ should be restricted to only one per a case-statement. + +__Basic BNF__ +```BNF +constant ::= +identifier ::= +type_name ::= +const_or_i ::= constant | identifier +case_simple ::= constant +is_Expr ::= + +dont_care ::= "__" +keyword_IS ::= "Is" +keyword_TO ::= "To" +keyword_WITH ::= "With" +keyword_As ::= "As" + +type_id ::= identifier | dont_care +type_ids ::= type_id | ( "{" type_id ("," type_id )* "}" ) +type_names ::= type_name | ( "(" type_name ( "," type_name )* ")" ) + +case_is ::= keyword_IS ( const_or_id | is_Expr ) +case_range ::= const_|_id keyword_To const_|_id + +case_type1 ::= type_names keyword_WITH "{" type_id? ("," type_id? ) * "}" +case_type0 ::= type_names keyword_As type_ids + +case_simpleExpr ::= case_simple | case_is | case_range +case_simpleExprs ::= case_simpleExpr ("," case_simpleExpr)* +case_complexExpr ::= case_simple | case_type0 +case_guard ::= case_complexExpr "When" expr + +case_statement ::= "Case" case_complex | case_guard +``` +Allow the current set _( < Roslyn Preview )_ of case statements which uses __As__ as the mechanism of introducing variables into scope. +```VB +Select Case X + Case 1 + Case 2 + Case 3,4 + Case Is < 1 + Case Is > 100 + Case 5 To 10 + Case 11 To 20, 31 To 40 +End Select +``` +The following are _restricted_ to only one per a case statement. + +__Type Matching__ +The capability of matching against a type or set of types +```VB +Select Case TypeOf ( objT ) ' <- Special form of Select Case + Case Integer As my_integer + Case Double As my_double + Case (Double,Integer) As ( my_double, my_integer ) + ' NOTE: This would be a an error as only one parameter was provided. + ' : by the Case TypeOf( ) statement +End Select + +Select Case TypeOf ( node.Left , node.Right ) ' <- Special form of Select Case + Case Integer As my_integer + ' NOTE: Invalid as more than one type argument was supplied + Case Double As my_double + ' NOTE: Invalis as more that one type argument was supplied + Case (Double,Integer) As ( my_double, my_integer ) + ' NOTE: my_double my_integer are in scope. +End Select +``` +The scoping of the introduced variable is restricted to the code statement itself and it's body. + +__Guarded Cases__ + +The body of the case statement is only select if the __predicate__ is true. + +```vb +Select Case x + Case 0 When y > 0 + Case 0 When y <= 0 + Case 1, 2, 3 When y > 0 + Case 1 When foo = true, 2 When foo = false + ' NOTE: Invalid only one guard per a case statement +End Select +``` +The invalid case-statement could always be expanded into to separate case -statements +```vb +Case 1 When Foo = True +Case 2 When foo = false +``` +If we discard the invalid case-statement what would the equivalent IF-based construct would be +```vb +If ( x = 0 ) Then + If ( y > 0 ) Then + ' Body of first case statement + End If +Else If ( x = 0 ) Then + IF ( y <= 0 ) Then + ' Body of second case statement + End If +Else IF (( x = 1) OrElse ( x =2 ) OrElse ( x = 3 ) ) Then + If ( y > 0 ) Then + ' Body of third case statement + End If +End If +``` + +```vb +Select Case TypeOf( node,Left , node.Right ) + Case (Integer, Integer ) As ( L, R ) When (L >= 0) AndAlso (R >= 0) + ' do this +End Select +``` +Let's looks at what the equivalent If-based construct would be +```vb +If ( TypeOf( node.Left ) Is Integer ) AndAlso + ( TypeOf( node.Right ) Is Integer ) Then + ' Introduce the variables + Dim L = DirectCast( node.Left , Integer ) + Dim R = DirectCast( node.Right , Integer ) + ' Since the introduced are in scope we can use then in the guard predicate. + If ( ( L >= 0 ) AndAlso ( R >= 0 ) Then + ' Body of case statement + End If +End If +``` + +The predicate of the guard can use any variable that is available and in scope at the particular section of code. +Hence the previous restriction on matching against type(s), which could (if it was allowed) is introduce variable of the same name but different types). + +- - - +#__With__ instead of __As__ +Same BNF as before expect that +```BNF +case_complexExpr ::= case_simple | case_type1 +``` + +Instead of using __As__ lets use and existing construct _With__ from object Initializer syntax with a tweak. + +```vb +Select Case TypeOf( node.Left, node.Right ) + Case ( Integer , Integer ) With {L, R} When ( (L>=0) AndAlso (R >= 0) ) +End Select +``` + +I think this form of the syntax is more VB-like . + +--- + +On Sep 15, 2014 at 6:08 PM @AdamSpeight2008 wrote: + +What is the current status of the Pattern Matching language feature? As the is seems to be a little quiet on the vb.net front. + +--- + +On Sep 17, 2014 at 4:35 AM @nmgafter wrote: + +We have a prototype of pattern matching (and records) for C# which has been described elsewhere. The design transcribes to VB very nicely but no work has started on a VB prototype. The proposal has not yet been considered by the language design committees for inclusion in any particular release of the languages. That discussion will begin after the current set of language changes have been shipped in a product. + +--- + +On Sep 17, 2014 at 6:13 AM @ADGreen wrote: + +**AdamSpeight2008 wrote:** +> What is the current status of the Pattern Matching language feature? As the is seems to be a little quiet on the vb.net front. + +Hey AdamSpeight2008, + +As nmgafter said, the pattern matching discussion is still early in the design life cycle, though we've actually been having conversations about pattern matching for both VB and C# back and forth for the last year or so and will continue to think about both as the discussion evolves. + +We think of pattern matching, records/tuples, and destructuring/match operators as part of a cohesive story that could advance the state of the .NET platform in the way that features like Generics, LINQ/lambdas, and Async have. Once you have a syntax for declaring libraries that expose these kinds of data structures and syntax in a language for consuming them, the way .NET developers design and interact with the ecosystem fundamentally leaps forward and platforms and libraries are rarely written in the same way again. + +As such, it would be critical to both our VB and C# developer communities to ensure that we deliver those features simultaneously to ensure both communities continue to have full access to the richness of the .NET ecosystem (as we have with other paradigm-shifting language features in prior releases). It is also why it's important to consider such features in coordination with our 1st-party platforms such as ASP.NET and the .NET Base Class Library so that the languages and .NET Framework continue to co-evolve together. + +On the topic of pattern matching specifically we've been most interested in how pattern matching could integrate with the existing VB Select Case construct. The prototype Select Case improvements we shipped as part of the "Roslyn" End User Preview for Visual Studio 2013 are actually an artifact of this very discussion. For both languages it's very important to us while designing features for Visual Studio "14" that we consider how they would work together with features we know we're strongly consider beyond VS "14" so that the languages continue to have cohesive feature sets and self-consistent styles. + +Regards, + +__Anthony D. Green__, Program Manager, __Visual Basic & C# Languages Team__ + +--- + +On Sep 18, 2014 at 2:21 PM @juliusfriedman wrote: + +In my opinion this only adds some syntactic sugar and in some very nitche cases and you shouldn't change old code you should adopt it if the scenario arrives and helps your paradigm, otherwise its just there. + +This is similar to exception filters and comes from the need to be able to have F# patterns implemented in C#. + +Lets consider the 'is' overload, this construct allows for special filtering by the compiler but is in no way more performant then simply filtering the clause yourself. + +E.g + +'if (x is Something) return (x as Something).Value > 0' + +Becomes something like + +'x is (Something, 1)' + +The problem anyone failed to realise is that when x is Something via the virtues of something else either via inheritance or interface and those properties are virtual this could be tricky to debug especially when each type may have a im implicit or explicit cast overload for Something. + +Additionally the 'Record' concept is nothing different than an anonymous type which has a type with the underlying. + +E.g. a Record of Something simply provides a wrapper around Record with your properties as immutable and a hashcode. (only again as syntactic sugar this time to support Primary constructors which have another set of gotchas, e.g. when the inheriting type defines a property which is generated by the derivative type, hiding occurs and you end up having to type everything out anyway) + +I wish that things which actually need attention were revisited e.g. events (maybe events which can be turned on and off via a property 'Enabled' and the ability to to stop propagation after a certain handler) + +Recursive function optimization or a tail keyword to force it to be applied. + + +Nonetheless interested to see where and how this gets used in reality. + +--- + +On Sep 19, 2014 at 3:49 AM @supercat wrote: + +**juliusfriedman wrote:** +> Recursive function optimization or a tail keyword to force it to be applied. + +Tail recursion should IMHO be applied only when a `tail return` statement is used. There are a variety of cases which may affect a compiler's ability to use tail recursion; program which requires tail recursion for correctness (i.e. because it would otherwise blow the stack) should explicitly say that, so that anything which would in future prevent tail recursion would cause a compile time error, rather than generating code that will bomb at run-time. + +--- + +On Sep 19, 2014 at 4:21 AM @juliusfriedman wrote: + +I proposed a tail return as well as a recursive keyword well before Roslyn was released, thanks for the comment! + +--- + diff --git a/meetings/2014/LDM-2014-07-01.md b/meetings/2014/LDM-2014-07-01.md new file mode 100644 index 0000000..b736010 --- /dev/null +++ b/meetings/2014/LDM-2014-07-01.md @@ -0,0 +1,87 @@ +# #Warning Disable + +## REQUEST + +Please add `#pragma` to VB so we can suppress warnings. This will be especially useful for Roslyn Diagnostics, where people will find many more warnings in their code. + +## RESOLUTION + +Add two new preprocessor directives: + +``` vb +#Disable Warning 40008, BC42358, "BC42356" +#Enable Warning 40008, BC42358, "BC42356" +``` + +## ALTERNATIVE SYNTAXES CONSIDERED + +``` vb +#pragma disable +#pragma enable + +#Disable ' sounds like you're disabling code, not warnings +#Enable + +#Ignore +#End Ignore ' block structure is worse than toggles + +#Disable Warning +#Enable Warning +``` + + +## DISCUSSION + +The tradition in VB and its rich history of quick-fixes is that you resolve warnings by FIXING YOUR CODE, e.g. by doing whatever the quickfix says, adding an explicit cast, adding a return statement, ... + +Q. Do people really use #pragma much in C# today? Hard to say. With new Roslyn diagnostics, will *more* people start using #pragma in C# ? Will people write Roslyn diagnostics that have a much higher false-positive rate than existing rules, for people who wish to be more careful, safe in the knowledge that it's easy to suppress? + +Q. Is there a downside to adding #pragma to VB for suppressing warnings? - no, not really + +Q. Should we add a feature which suppresses the warning merely for the next line, rather than for the rest of the file? -- This isn't feasible in C# where lines don't really exist semantically. It is feasible for VB. Disadvantage: would confusing for LINQ, and not refactoring-safe. Advantage: if the lightbulb/quickfix appears for a warning, and when you chose the option to "suppress warning" then it only has to insert a single line to "suppress-warning-on-next-line", rther than having to insert `#ignore` before and `#restore` after. Conclusion: No we won't add this next-line-suppression feature. + +Q. Is it good to disable/enable on a "block" basis like `#Ignore / #End Ignore` ? Or on a toggle basis like `#Disable Warning / #Enable Warning` ? Well, with blocks you can't do overlapping regions. And you can't easily disable warnings for an entire method but then re-enable them for a small critical region inside (even though that's a weird scenario). We believe overall that toggles are nicer. They have a lot of engineering, thought and experience behind them. + +Q. For the ``, should quotation marks be obligatory? - No. You can use any identifier or integer. It's only if the warning-id has spaces or other weird characters that you'd need quotation marks. + +Q. Should we allow pure integers for the ``, with the expectation that they disable intrinsic VB compiler warnings? All user-defined warnings (that come through Roslyn diagnostics) would have error codes that aren't pure integers. -- Yes, let's allow integers, just like C#, and just like the VB command-line. It will be a common case. + +Q. Will the Roslyn diagnostics APIs allow people to define error codes that are just pure numbers, even though they'll be impossible to disable them? -- No real need. Diagnostic-authors will learn soon enough not to do this. + +Q. Should we allow you to disable/enable command-separated lists of warnings in a single `#Disable Warning / #Enable Warning` directive? -- Yes. + +Q. Preprocessor directives look ugly because they're always left-aligned. Can we make it so preprocessor directives can be indented? There's a uservoice request for it. -- Not discussed; left for another day. + + +# Back-compat break with interface implementations + +We also discussed another topic to do with ambiguity between unrelated interfaces. Discussion is in this thread: +https://roslyn.codeplex.com/discussions/570975 + +# Update: VB LDM 2014.07.01 + +We re-visited #Disable Warning. The summary for VB is: + +* Use the syntax `#Disable Warning id1, id2, ...` +* The warning IDs must parse as per the same rules of VB identifiers, and are case-insensitive. That rules out plain numbers `#Disable Warning 4008` (instead you must disable BC4008) and it rules out strings `#Disable Warning "Don't Use Capitals!"`. That means you can't disable warnings that have fancy error codes with punctuation and so on. +* This will guide users to the thought that they can right-click, find all references, and opens up the mental door to other refactorings/tooling +* We will restrict what identifiers can be produced at the production side too, when authoring analyzers +* We used to give BC2026 “that warning can’t be configured” when you try to nowarn or warnaserror on non-existent warnings (and also on certain named warnings e.g. 2026). But that wouldn’t work for diagnostic warning codes. So let’s no longer emit BC2026 ever. Instead, if you try to disable or enable a non-existing warning, simply do nothing. +* Stretch goal: the IDE could grey out all identifiers that have been disabled but are never actually generated by the code + +C# will be mostly similar, but +* C# will support disabling warnings/errors by number as well as by identifier, and is case sensitive. It has to support disabling numbers for back-compat. If you disable warning number 4008, that's equivalent to disabling identifier CS4008. +* As with VB, we stop validating whether the warning code is a legal compiler warning ID (both on the command-line and inside #pragma) - CS1691 will no longer be reported for C#. + +Things that fall out for free: +* No changes required to disallow Hexadecimal, Octal, Long etc. literals +* No need to worry about making BC2026 unerrorable + +For the future: +* Update lightbulb suppression to always generate #pragma / #disable with identifiers. (Do we need to check that the id will be a legal identifier before spitting the code?) +* Fix /nowarn and /warnaserror command-line switches to support custom diagnostics +* Implement grey out for “unused” ids in #pragma, #Disable (plus corresponding code fix) +* Have some way for users to get from a reported warning id / from a warning id used in #pragma or #Disable to a description of the diagnostic +* Support for qualified identifiers that use dots (only if customers ask for it) + + diff --git a/meetings/2014/LDM-2014-10-01.md b/meetings/2014/LDM-2014-10-01.md new file mode 100644 index 0000000..e87a795 --- /dev/null +++ b/meetings/2014/LDM-2014-10-01.md @@ -0,0 +1,595 @@ +There were two agenda items... +1. Assignment to readonly autoprops in constructors (we fleshed out details) +2. A new compiler warning to prevent outsiders from implementing your interface? (no, leave this to analyzers) + +# Assignment to readonly autoprops in constructors + +```cs +public struct S { + public int x {get;} + public int y {get; set;} + public Z z {get;} + + public S() { + x = 15; + y = 23; + z.z1 = 1; + } +} + +public struct Z { int z1; } +``` + +_What are the rules under which assignments to autoprops are allowed in constructors?_ + +__Absolute__ We can't be more permissive in what we allow with readonly autoprops than we are with readonly fields, because this would break PEVerify. (Incidentally, PEVerify doesn't check definite assignment in the constructor of a struct; that's solely a C# language thing). + +__Overall principle__ When reading/writing to an autoprop, do we go via the accessor (if there is one) or do we bypass it (if there is one) and access the underlying field directly? +_Option1:_ language semantics say the accessor is used, and codegen uses it. +_Option2:_ in an appropriate constructor, when there is a "final" autoprop (either non-virtual, or virtual in a sealed class), access to an autoprop _means_ an access to the underlying field. This meaning is used for definite assignment, and for codegen. Note that it is semantically visible whether we read from an underlying field vs through an accessor, e.g. in `int c { [CodeSecurity] get;}` +_Resolution: Option1_. Under Option2, if you set a breakpoint on the getter of an autoprop, gets of it would not hit the breakpoint if they were called in the constructor which is weird. Also it would be weird that making the class sealed or the autoprop non-virtual would have this subtle change. And things like Postsharper wouldn't be able to inject. All round Option2 is weird and Option1 is clean and expected. + +__Definite Assignment__. Within an appropriate constructor, what exactly are the rules for definite assignment? Currently if you try to read a property before _all_ fields have been assigned then it says CS0188 'this' cannot be used before all fields are assignment, but reading a field is allowed so long as merely that field has been assigned. More precisely, within an appropriate constructor, for purposes of definite assignment analysis, when does access of the autoprop behave as if it's an access of the backing field? +_Option1_: never +_Option2_: Only in case of writes to readonly autoprops +_Option3_: In the case of writes to all autoprops +_Option4_: In the case of reads and writes to all autoprops +_Resolution: Option4_. This is the most helpful to developers. You might wonder what happens if it's a virtual autoprop and someone overrides getter or setter in derived types in such a way that would violate the definite assignment assumptions. But for structs there won't be derived types, and for classes the semantics say that all fields are assigned to default(.) so there's no difference. + +__Piecewise initialization of structs__. In the code above, do we allow `z.z1 = 15` to assign to the _field_ of a readonly struct autoprop? +_Option1:_ Yes by threating access to "z" for purposes of definite assignment as an access of the underlying field. +_Option2: _ No because in `z.z1` the read of `z` happens via the accessor as per the principle above, and thus returns an rvalue, and hence assignment to `z.z1` can't work. Instead you will have to write `z = new Z(...)`. +_Resolution: Option2_. If we went with Option1, then readonly autoprops would end up being more expressive than settable autoprops which would be odd! Note that in VB you can still write `_z.z1 = 15` if you do want piecewise assignment. + +__Virtual__. What should happen if the readonly autoprop is virtual, and its getter is overridden in a derived class? +_Resolution:_ All reads of the autoprop go via its accessor, as is already the case. + +__Semantic model__. In the line `x = 15` what should the Roslyn semantic model APIs say for the symbol `x` ? +_Resolution:_ they refer to the property x. Not the backing field. (Under the hood of the compiler, during lowering, if in an appropriate constructor, for write purposes, it is implicitly transformed into a reference to the backing field). More specifically, for access to an autoprop in the constructor, +1. It should _bind_ to the property, but the property should be treated as a readable+writable (for purposes of what's allowed) in the case of a readonly autoprop. +2. The definite assignment behavior should be as if directly accessing the backing field. +3. It should code gen to the property accessor (if one exists) or a field access (if not). + +__Out/ref arguments in C#__. Can you pass a readonly autoprop as an out/ref argument in C#? +_Resolution: No_. For readonly autoprops passed as _ref_ arguments, that wouldn't obey the principle that access to the prop goes via its accessor. For passing readonly autoprops as _out_ arguments with the hope that it writes to the underlying field, that wouldn't obey the principle that we bind to the property rather than the backing field. For writeonly autoprops, they don't exist because they're not useful. + +__Static readonly autoprops__ Should everything we've written also work for static readonly autoprops? +_Resolution: Yes._ Note there's currently a bug in the native compiler (fixed in Dev14) where the static constructor of a type G is able to initialize static readonly fields in specializations of G e.g. `G.x=15;. The CLR does indeed maintain separate storage locations for each static readonly fields, so `G.g` and `G.g` are different variables. (The native compiler's bug where the static constructor of G could assign to all of them resulted in unverifiable code). + +__VB rules in initializers as well as constructors__. VB initializers are allowed to refer to other members of a class, and VB initializers are all executed during construction time. Should everything we've said about behavior in C# constructors also apply to behavior in VB initializers? +_Resolution: Yes_. + +__VB copyback for ByRef parameters__. In VB, when you pass an argument to a ByRef parameter, then either it passes it as an lvalue (if the argument was a local variable or field or similar) or it uses "copy-in to a temporary then invoke the method then copy-out from the temporary" (if the argument was a property), or it uses "copy-in to a temporary then invoke the method then ignore the output" (if the argument was an rvalue or a constant). What should happen when you pass a readonly autoprop to a ByRef parameter? +_Option1:_ Emit a compile-time error because copyback is mysterious and bites you in mysterious ways, and this new way is even more mysterious than what was there before. +_Option2:_ Within the constructor/initializers, copy-in by reading via the accessor, and copy-back by writing to the underlying field. Elsewhere, copy-in with no copy-out. Also, just as happens with readonly fields, emit an error if assignment to a readonly autoprop happens in a lambda in a constructor (see code example below) +_Resolution: Option2_. Exactly has happens today for readonly fields. Note incidentally that passing a readonly autoprop to a ByRef parameter will have one behavior in the constructor and initializers (it will do the copy-back), and will silently have different behavior elsewhere (it won't do any copy-back). This too is already the case with readonly fields. On a separate note, developers would like to have feedback in some cases (not constants or COM) where copyback in a ByRef argument isn't done. But that's not a question for the language design meeting. + +__VB copyin for writeonly autoprops__. VB tentatively has writeonly autoprops for symmetry, even though they're not useful. What should happen when you pass a writeonly autoprop as a ByRef argument? +_Resolution: Yuck._ This is a stupid corner case. Notionally the correct thing is to read from the backing field, and write via the setter. But if it's easier to just remove support for writeonly autoprops, then do that. + +```vb +Class C + ReadOnly x As Integer = 15 + + Public Sub New() + f(x) + Dim lamda = Sub() + f(x) ' error BC36602: 'ReadOnly' variable + ' cannot be the target of an assignment in a lambda expression + ' inside a constructor. + End Sub + End Sub + Shared Sub f(ByRef x As Integer) + x = 23 + End Sub +End Class +``` + +We discussed a potential new error message in the compiler. + +__Scenario:__ Roslyn ships with ISymbol interface. In a future release it wants to add additional members to the interface. But this will break anyone who implemented ISymbol in its current form. Therefore it would be good to have a way to prevent anyone _else_ from implementing ISymbol. That would allow us to add members without breaking people. + +Is this scenario widespread? Presumably, but we don't have data and haven't heard asks for it. There are a number of workarounds today. Some workarounds provide solid code guarantees. Other workarounds provide "suggestions" or "encouragements" that might be enough for us to feel comfortable breaking people who took dependencies where we told them not to. + +__Counter-scenario:__ Nevertheless, I want to _MOCK_ types. I want to construct a mock ISymbol myself maybe using MOQ, and pass it to my functions which take in an ISymbol, for testing purposes. I still want to be able to do this. (Note: MOQ will automatically update whenever we add new members to ISymbol, so users of it won't be broken). + + +__Workarounds__ + +1. Ignore the problem and just break people. +2. Like COM, solve it by adding new incremental interfaces ISymbol2 with the additional members. As Adam Speight notes below, you can make ISymbol2 inherit from ISymbol. +3. Instead of interfaces, use abstract classes with internal constructors. Or abstract classes but never add abstract methods to it; only virtual methods. +4. Write documentation for the interface, on MSDN or in XML Doc-Comments, that say "Internal class only; do not implement it". We see this for instance on ICorThreadpool. +5. Declare a method on the interface which has an internal type in its signature. The CLR allows this but the language doesn't so it would have to be authored in IL. Every type which implements the interface would have to provide an implementation of that method. +6. Write run-time checks at the public entry points of key Roslyn methods that take in an ISymbol, and throw if the object given was implemented in the wrong assembly. +7. Write a Roslyn analyzer which is deployed by the same Nuget package that contains the definition of ISymbol, and have this analyzer warn if you're trying to implement the interface. This analyzer could be part of Roslyn, or it could be an independent third-party analyzer used by many libraries. + + +__Proposal:__ Have the compiler recognize a new attribute. Given the following code +```cs +[System.Runtime.CompilerServices.InternalImplementationOnly] interface I<...> {...} +``` +it should be a compile-time warning for a type to implement that interface, directly or indirectly, unless the class is in the same assembly as "I" or is in one of its InternalsVisibleTo friends. It will also be a compile-time error for an interface to inherit from the interface in the same way. Also, we might ask for the .NET Framework team to add this attribute in the same place as System.Runtime.CompilerServices.ExtensionAttribute, and CallerMemberNameAttribute. But doing it isn't necessary since the compiler will recognize any attribute with that exact fully-qualified name and the appropriate (empty) constructor. + +Note that this rule would not be cast-iron, since it won't have CLR enforcement. It would still be possible to bypass it by writing IL by hand, or by compiling with an older compiler. But we're not looking for cast-iron. We're just looking for discouragement strong enough to allow us to add members to ISymbol in the future. (In the case of ISymbol, it's very likely that people will be using Roslyn to compile code relating to ISymbol, but that doesn't apply to other libraries). + + +__Resolution:__ Workaround #7 is a better option than adding this proposal to the language. + + + +--- +--- + +On Oct 2, 2014 at 12:42 AM @AdamSpeight2008 wrote: + +Wny cant you use inherited interfaces? + +``` +Interface ISymbolExtended inherits ISymbol +'new members +End interface +``` + +--- + +On Oct 2, 2014 at 12:46 AM @lwischik wrote: + +**AdamSpeight2008 wrote:** +> Why cant you use inherited interfaces? + +You can! That's workaround #2. (But then every single method that takes in or returns an ISymbol would have to change to ISymbolExtended, which would be a pain). It's just a direction towards a messy type system that's caused by back-compat considerations, not by good-design considerations. + + +--- + +On Oct 2, 2014 at 12:53 AM @AdamSpeight2008 wrote: + +Or you utilise that fact it inherits from ISymbol and thus an ISymbol and do type matching. + +``` +Select Type someSymbol + Case s As ISymbolExtended + Case s As ISymbol +End Select +``` + + + +--- + +On Oct 2, 2014 at 3:16 AM @qrli wrote: + +The System.Runtime.CompilerServices.InternalImplementationOnly attribute does be useful to enterprise code, where interfaces are often used as the API of modules. Some are inappropriate usage only because of Java influence or recommendation from some books which are not about API. But _testability and mockability is the real benefit_, even when it looks wrong from API point of view. Anyway, the result is that interfaces are used a lot as API only, but not intended to be implemented by consumer code, except for mocking. + +So, we suffer the same interface extension issue which you mentioned. It would be very useful if we can mark the intention of the interfaces, to give a warning that we don't guarantee the compatibility of this interface. + +But, I also agree that it may not help as expected. For developers who do not think in API point of view, they will not consider adding this attribute either. They will simply continue commiting no binary compatiblity and pushing the cost to API consumer side. + +--- + +On Oct 2, 2014 at 7:07 PM @AdamSpeight2008 wrote: + +__@lwischik__ + +Another option is to add a version number to the interface. + +``` +[version(1.0.0.0)] +interface ISymbol { } + +[version(2.0.0.0)] +interface ISymbol { } <: ISymbol [version(1.0.0.0)] +``` +Overloaded interfaces. + +Then require the implementer to specify which version they are implementing. +``` +class Foo implements ISymbol [version(2.0.0.0)] +{ } +``` + +--- + +On Oct 2, 2014 at 9:25 PM @supercat wrote: + +**lwischik wrote:** +> There were two agenda items... +> 1. Assignment to readonly autoprops in constructors (we fleshed out details) + +Was any consideration given to the idea of having a concise syntax for autoproperties with explicitly-named backing fields (which could either be read-only or not, as convenient)? +> 2. A new compiler warning to prevent outsiders from implementing your interface? (no, leave this to analyzers) + +At what level are the rules regarding visibility of method parameters enforced? Are those only enforced in the compiler or would the class loader enforce those as well? I've long thought there should be a means by which an interface or concrete class could limit implementation or derivation to the assembly in which it resides. While it would be possible for a class to have a private interface which derives from the public-facing one, and cast to the private type all references of the interface type which are received from outside code, but that really seems rather hokey. If members whose types were not visible to outside code were themselves not visible to outside code, that would resolve the issue. + + +>On a separate note, developers would like to have feedback in some cases (not constants or COM) where copyback in a ByRef argument isn't done. But that's not a question for the language design meeting. + +When would such a question be addressed? The fact that VB.NET silently copies values passed as `ByRef` exists, from what I can tell, as concession toward VB6 compatibility; I would expect the majority of such usages are erroneous. IMHO, attributes should be defined for properties and method parameters such that, based upon the attributes of each the compiler could decide to either: + +1. Put the property value in a temporary, pass a reference to that, and then discard the temporary. +2. Put the property value in a temporary, pass a reference to that, and then copy the temporary back to the property. +3. Refuse compilation [as should happen if, e.g., anything other than a mutable variable is passed to `Interlocked.CompareExchange`. + +It irks me that mutable structures are branded as "evil" when the fundamental problem is the inability to prevent compilers from silently transforming code which shouldn't compile into code which compiles but doesn't work. + +--- + +On Oct 2, 2014 at 10:39 PM @lwischik wrote: + +**supercat wrote:** +> Was any consideration given to the idea of having a concise syntax for autoproperties with explicitly-named backing fields (which could either be read-only or not, as convenient)? + +No! Could you flesh out that thought some more? Motivating scenarios? Honestly, if you're adding syntax to have explicit names backing fields, then it's not a far step from having explicit backing fields themselves... + +VB continues to have private backing fields with a prepended underscore and they are readwrite (even for readonly autoprops) and you can use them from code even though they don't show up in intellisense. Given this, there's no great benefit in being able to come up with your own names. C# continues to have private backing fields with a name that's unutterable in user-code, and will use readonly backing fields for readonly autoprops. + + +> At what level are the rules regarding visibility of method parameters enforced? Are those only enforced in the compiler or would the class loader enforce those as well? If members whose types were not visible to outside code were themselves not visible to outside code, that would resolve the issue. + +1. When doing meta-import, VB/C# compilers simply decline to meta-import certain private members in order to save space. I think they meta-import private types, but not private members of classes, but I can't remember the exact details. + +2. If VB/C# compilers can't resolve every single type in a member's signature (either because it's private, or because it was defined in a reference that wasn't passed /r: to the compiler) then the compiler marks the member as bad. + +3. When doing overload resolution, if one of the candidates is bad, then the compiler (as of Roslyn) declines to make any attempt at overload resolution. That's because you wouldn't want the choice of method to be changed merely by adding or removing a reference. + +What you're describing seems about the same as workaround #5 (depends on the exact requirements vs meaning of "visibility" in your sentence). + +I'm not sure what it would even mean for the class loader or JITer to enforce visibility...! + + +> When would such a question be addressed? The fact that VB.NET silently copies values passed as `ByRef` exists, from what I can tell, as concession toward VB6 compatibility; I would expect the majority of such usages are erroneous. + +That's an interesting thought. We at the LDM hadn't thought that. I guess we positively like the ability to pass properties ByRef. I think it's nice that you can have an autoprop and use it how you would a field, e.g. `int.TryParse(s, MyProperty)`! But from what you said, and the example attributes you gave, we shouldn't take this for granted in future design work. + +When would such a question be addressed? The question was "how should developers get the feedback they want when they pass arguments to a ByRef parameter and it does one of the many mysterious VB language behaviors like copy-in, copy-out, ignore-the-copy-out ?". I think that should start life as an analyzer written by someone who's passionate about it, either on the VB team or the public. + + +> It irks me that mutable structures are branded as "evil" when the fundamental problem is the inability to prevent compilers from silently transforming code which shouldn't compile into code which compiles but doesn't work. + +The compiler can never do this alone. Swift solves it by adding a new keyword "mutable" on struct methods that mutate its structure, and enforcing both what can be done inside the method and who/when can call it. + +What VB/C# cases are you thinking of with evil mutable structs? I think of these: + +* When a local variable is readonly (e.g. the loop control variable in Using) then potentially-mutating methods are defensively performed on a copy of a struct rather than on the struct itself. + +* In the implementation of the "await" operator, if your awaiter type is a struct and its OnCompleted method is mutating, then mutations may or may not be discarded depending on whether this is the first cold await or a subsequent one. Actually we initially built it to copy the struct defensively, to support mutating struct OnCompleted methods in all cases, but it had appreciable perf overhead that didn't seem justified. + + + + +--- + +On Oct 3, 2014 at 1:08 AM @supercat wrote: + +**lwischik wrote:** +> No! Could you flesh out that thought some more? Motivating scenarios? Honestly, if you're adding syntax to have explicit names backing fields, then it's not a far step from having explicit backing fields themselves... + +A possible scenario would be a class with a `Bounds` property of type `Rectangle`. The class may benefit from being able to access the members of `Bounds` directly, even though it cannot expose the ability to do so through the property. In C# the backing field name is "unpronounceable". + +As for "not far from having explicit backing fields", that might not be so bad if property syntax didn't gobble up so much vertical space in VB. In most cases I think vb.net code is more compact vertically than C# code [an "if/then/else" with two statements in each branch, for example, takes 7 lines; in C# with most bracing conventions it would take at least eight if not ten] but properties are a major exception. + +> VB continues to have private backing fields with a prepended underscore and they are readwrite (even for readonly autoprops) and you can use them from code even though they don't show up in intellisense. Given this, there's no great benefit in being able to come up with your own names. C# continues to have private backing fields with a name that's unutterable in user-code, and will use readonly backing fields for readonly autoprops. + +If it's accessible in vb, that's good, though my understanding is that such underscore names are considered a quasi-undocumented feature [am I mistaken in that]? + +> I'm not sure what it would even mean for the class loader or JITer to enforce visibility...! + +Suppose I were to try to code: + + internal class Foo { ... } + public interface Bar { void Wowzo(Foo hey); } + +At present, C# would reject that because the declaration of Wowzo is exposing internal class `Foo`. Suppose, however, that the C# compiler were to ignore that condition and proceded to generate data for `Bar` in the same was as it would if `Foo` had been a public class. What would happen? + +> > When would such a question be addressed? The fact that VB.NET silently copies values passed as `ByRef` exists, from what I can tell, as concession toward VB6 compatibility; I would expect the majority of such usages are erroneous. +> +> That's an interesting thought. We at the LDM hadn't thought that. I guess we positively like the ability to pass properties ByRef. I think it's nice that you can have an autoprop and use it how you would a field, e.g. `int.TryParse(s, MyProperty)`! But from what you said, and the example attributes you gave, we shouldn't take this for granted in future design work. + +One of my peeves with regard to language design is that the language designers seem to worry too much on trying to perfect rules for the contexts in which certain things should be allowed and others not, rather than letting programmers specify contexts in which certain things should be allowed and others not. For example, a lot of code would be a lot more readable if parameters of type `Single` could be marked with an attribute specifying "allow implicit conversion from Double`, and many bugs could be caught at compile time if parameters could be marked with attributes to *disallow* certain conversions (e.g. mark the parameters to `Object.ReferenceEquals` to disallow any boxing conversions). + +> > It irks me that mutable structures are branded as "evil" when the fundamental problem is the inability to prevent compilers from silently transforming code which shouldn't compile into code which compiles but doesn't work. +> +> The compiler can never do this alone. Swift solves it by adding a new keyword "mutable" on struct methods that mutate its structure, and enforcing both what can be done inside the method and who/when can call it. + +What difficulty would there be in having the compiler reject any attempt to call a struct method which has an `MutatesThis()` attribute on a read-only temporary, and any attempt to call a struct method which has a `MutatesThisAtomically()` attribute on anything other than a genuine mutable storage location? + + +--- + +On Oct 3, 2014 at 2:16 AM @lwischik wrote: + +**supercat wrote:** +> If it's accessible in vb, that's good, though my understanding is that such underscore names are considered a quasi-undocumented feature [am I mistaken in that]? + +They're in the spec $9.7.4 and we're never going to remove them! But not going to show them in intellisense either because they'd be confusing for most cases. + +> At present, C# would reject that because the declaration of Wowzo is exposing internal class `Foo`. Suppose, however, that the C# compiler were to ignore that condition and proceded to generate data for `Bar` in the same was as it would if `Foo` had been a public class. What would happen? + +This is exactly workaround #5. It's fine from the CLR perspective. I'm not aware of any CLR enforcement of accessibility, but I haven't explored much. + +> One of my peeves with regard to language design is that the language designers seem to worry too much on trying to perfect rules for the contexts in which certain things should be allowed and others not, rather than letting programmers specify contexts in which certain things should be allowed and others not. For example, a lot of code would be a lot more readable if parameters of type `Single` could be marked with an attribute specifying "allow implicit conversion from Double`, and many bugs could be caught at compile time if parameters could be marked with attributes to *disallow* certain conversions (e.g. mark the parameters to `Object.ReferenceEquals` to disallow any boxing conversions). + +That's a nice principle. I'm wary of how it interacts with my pet principle "avoid counter-measures". The hero is trapped in a room and a bomb is going to go off. The dialogue goes like this: + +Hero: Computer, open the door +Computer: I'm sorry Dave your nemesis placed an override which doesn't let me do that +Hero: Computer, remove the override on the door-opening +Computer: I'm sorry Dave your nemesis placed an override which doesn't let me remove the override +Hero: Computer, remove the override on the override on the door-opening +Computer: Okay +Hero: Computer, remove the override on the door-opening +Computer: Okay +Hero: Computer, open the door +Computer: Okay + +We have that with "warnaserror:always". But then some warnings are marked with "this can never be treated as an error". + +In your case, the performance-hungry coder would want to make sure there are no implicit conversions between floating point types. Does this concern override the parameter attribute or not? + +> What difficulty would there be in having the compiler reject any attempt to call a struct method which has an `MutatesThis()` attribute on a read-only temporary, and any attempt to call a struct method which has a `MutatesThisAtomically()` attribute on anything other than a genuine mutable storage location? + +That sounds like a better candidate for an analyzer. If it were to go into the language itself I'm sure we'd want it driven by keyword rather than attribute. And then we'd fret that the whole thing is a heavy concept overhead for something that only a small minority of people would want. + + +--- + +On Oct 3, 2014 at 10:39 AM @Przemyslaw wrote: + +**lwischik wrote:** +> __Out/ref arguments in C#__. Can you pass a readonly autoprop as an out/ref argument in C#? +> _Resolution: No_. For readonly autoprops passed as _ref_ arguments, that wouldn't obey the principle that access to the prop goes via its accessor. For passing readonly autoprops as _out_ arguments with the hope that it writes to the underlying field, that wouldn't obey the principle that we bind to the property rather than the backing field. For writeonly autoprops, they don't exist because they're not useful. + +What about passing readonly autoprop as out parameter inside constructor body? + +--- + +On Oct 3, 2014 at 3:57 PM @supercat wrote: + +**lwischik wrote:** +> **supercat wrote:** +> > Was any consideration given to the idea of having a concise syntax for autoproperties with explicitly-named backing fields (which could either be read-only or not, as convenient)? +> +> No! Could you flesh out that thought some more? Motivating scenarios? Honestly, if you're adding syntax to have explicit names backing fields, then it's not a far step from having explicit backing fields themselves... + +Upon some further consideration, I think I realized what I'm really after. How would you like something like + + Protected myBounds As Rectangle + Public ReadOnly Property Bounds As Rectangle _ + Gets myBounds Implements IReadableBounds.Bounds + Public ReadOnly Property X As Int32 Gets myBounds.X + Public ReadOnly Property Y As Int32 Gets myBounds.Y + Public Overridable ReadOnly Property NumberOfSides As Int32 Gets 4 + +I wasn't so much interested in "naming" the backing field as I was in having a concise syntax for the common situation where the getter is nothing more than a single expression. I would guess that 90%+ of readonly properties are nothing more than a single return statement. I would consider code like the above to be not just more compact, but also clearer than a bunch of five-line property declarations. Even in C#, I think `public readonly Rectangle Bounds {get myBounds;}` would be a little nicer than `public readonly Rectangle Bounds {get { return myBounds;} }`. + +--- + +On Oct 3, 2014 at 4:58 PM @supercat wrote: + +**lwischik wrote:** +> They're in the spec $9.7.4 and we're never going to remove them! But not going to show them in intellisense either because they'd be confusing for most cases. + +It's good to know it's documented, though I think much of the confusion stems from the fact that there's no distinction between declaring a public property with the intention that internal code will use the backing field directly, or declaring a public property with the intention that all access will be through the property. If what code really wants is: + + Dim _Bounds As Rectangle + Public ReadOnly Bounds As Rectangle Gets _Bounds + +then allowing code to actually *say* that would seem nicer than having code simply say `Public ReadOnly Bounds As Rectangle` and then having code use `_Bounds` without having explicitly declared it. I would guess that 90% of readonly properties could fit the `Gets` syntax shown above, but don't think a supermajority of those would want the backing field to be private and mutable. Many would want it protected and/or internal ("Friend"), and many would want the backing field to be read-only. + +> > At present, C# would reject that because the declaration of Wowzo is exposing internal class `Foo`. Suppose, however, that the C# compiler were to ignore that condition and proceded to generate data for `Bar` in the same was as it would if `Foo` had been a public class. What would happen? +> +> This is exactly workaround #5. It's fine from the CLR perspective. I'm not aware of any CLR enforcement of accessibility, but I haven't explored much. + +The link is meaningless and I don't know what you're referring to. If the CLR would simply ignore any members that were not usable from the type that was trying to use them, then I would consider a means of declaring such members as being semantically useful. + +> That's a nice principle. I'm wary of how it interacts with my pet principle "avoid counter-measures". The hero is trapped in a room and a bomb is going to go off. The dialogue goes like this: + +I think a more common situation would be: + +> Dave tries to open hatch from bedroom to residential hallway. It won't open. +> "Are you sure you want to open that hatch? Some hatches are dangerous." +> "Yes, Hal." +> Dave tries to open hatch from residential hallway into dining area. It won't open. +> "Are you sure you want to open that hatch? Some hatches are dangerous." +> "Yes, Hal." +> Dave tries to open hatch from dining area into service corridor. It won't open. +> "Are you sure you want to open that hatch? Some hatches are dangerous." +> "Yes, Hal." [with increasing annoyance] +> Dave tries to open hatch from service corridor into evacuated airlock. It won't open. +> "Are you sure you want to open that hatch? Some hatches are dangerous." +> "Yes, Hal. Just open the [bleep]ing door." +> "Whatever you say, Dave." + +> In your case, the performance-hungry coder would want to make sure there are no implicit conversions between floating point types. Does this concern override the parameter attribute or not? + +If I had my druthers, .NET would have included separate types for IEEE64 and Real; IEEE32 and ShortReal. The IEEE types would have been coercible(*) directly to or from the physically-identical Real and ShortReal but would not have supported implicit conversions in any other context, while the Real and ShortReal types would have been mutually convertible. Additionally, I would have had arithmetic operations on ShortReal yield a Real result except in scenarios where the result would be *immediately* assigned to a ShortReal; in that scenario, the compiler would be allowed to perform the computation as either Real or ShortReal, whichever would be faster. There are many situations where performing the computation `f1 = f2+f3+f4;` entirely with `Single` will yield somewhat less accurate results than performing the computation as `Double` and then rounding the result to `Single`; there are some where using `Single` will yield results that are grossly inaccurate. There are relatively few cases where computation as `Double` would give a worse result, and in those specific cases I would suggest that the code should use a "strict IEEE" type to force a compiler to perform all operands with `Single` [I believe that presently the only way to ensure that computations are actually done as single would be `f1 = CSng(f2+f3)+f4;` + +Since .NET doesn't include distinct types, I would suggest that attributes exist which would indicate when parameters and variables should be regarded as looser or stricter types, with stricter taking precedence. Anyone who wants to convert between a "strict" type and anything with a different physical representation would be required to use a cast, whether going from `Double` to `Single`, `Single` to `Double`, `Integer` to `Single`, etc. + +(*) I would consider a value to be "directly coerced" to a type when it is assigned to a variable of that type, or is passed as the kth parameter out of n to a method for which every overload that handles n parameters either has a kth parameter of that same type, or has a kth parameter of a type to which the value is niether implicitly nor explicitly convertible. + +> > What difficulty would there be in having the compiler reject any attempt to call a struct method which has an `MutatesThis()` attribute on a read-only temporary, and any attempt to call a struct method which has a `MutatesThisAtomically()` attribute on anything other than a genuine mutable storage location? +> +> That sounds like a better candidate for an analyzer. If it were to go into the language itself I'm sure we'd want it driven by keyword rather than attribute. And then we'd fret that the whole thing is a heavy concept overhead for something that only a small minority of people would want. + +From what I understand, for purposes of multi-language interop and CLS compliance, it would need to be an attribute. That in no way implies that a language couldn't also offer a keyword which sets the attribute (I think languages should), but since any keyword will have to apply an attribute to let outside code know of the restriction, and languages will have to recognize the attribute to honor restrictions requested in outside code, I think it makes sense to first define the attribute. + +As for the extent to which it would be wanted, there are quite a few places in the .NET framework where it would be appropriate, such as the instance methods on `Stopwatch`, and I think there are a lot of situations where it would be clearer to write: + + MyStruct.ChangeSomehow(); + +than either + + MyStruct = MyStruct.WithSomeChange(); + +or + + TypeOfStruct.ChangeSomehow(ref MyStruct); + +but programmers are effectively pressured into requiring one of the latter forms because of the lack of any means by which the former method may be written safely. + +--- + +On Oct 3, 2014 at 5:06 PM @lwischik wrote: + +**Przemyslaw wrote:** +> **lwischik wrote:** +> > __Out/ref arguments in C#__. Can you pass a readonly autoprop as an out/ref argument in C#? +> > _Resolution: No_. For readonly autoprops passed as _ref_ arguments, that wouldn't obey the principle that access to the prop goes via its accessor. For passing readonly autoprops as _out_ arguments with the hope that it writes to the underlying field, that wouldn't obey the principle that we bind to the property rather than the backing field. For writeonly autoprops, they don't exist because they're not useful. +> +> What about passing readonly autoprop as out parameter inside constructor body? + +That was the answer "no". Our principle is that for language purposes when you write the syntax `foo(out x)`, the symbol `x` binds to the property regardless of whether it's an autoproperty in the constructor or not. This binding applies to the rule "only lvalues are allowed for out arguments", which generates an error. The fact that x ends up being written to via its backing field is much lower level. + +Our way of thinking was more like "for purposes of definite assignment, autoprops behave like fields; but for all other purposes, readonly autoprops behave as if they had an imaginary 'getter' for the duration of the constructor." + +--- + +On Oct 3, 2014 at 5:28 PM @supercat wrote: + +**Przemyslaw wrote:** +> What about passing readonly autoprop as out parameter inside constructor body? + +An `out` parameter is really a `ref` parameter in disguise, except that the compiler is willing to ignore the fact that a variable passed as an `out` parameter is uninitialized. Interestingly, even when passing a variable as an `out` parameter to virtual method, the C# compiler does nothing to prevent an implementation written in another language from reading the previous contents of that variable (such as either clearing the variable before making the call, or creating a blank temporary variable and passing a `ref` to that). Passing the backing field of a class as an `out` parameter would allow outside code to read its value without using the property getter. + +--- + +On Oct 3, 2014 at 5:38 PM @Halo_Four wrote: + +**supercat wrote:** +> **Przemyslaw wrote:** +> > What about passing readonly autoprop as out parameter inside constructor body? +> +> An `out` parameter is really a `ref` parameter in disguise, except that the compiler is willing to ignore the fact that a variable passed as an `out` parameter is uninitialized. Interestingly, even when passing a variable as an `out` parameter to virtual method, the C# compiler does nothing to prevent an implementation written in another language from reading the previous contents of that variable (such as either clearing the variable before making the call, or creating a blank temporary variable and passing a `ref` to that). Passing the backing field of a class as an `out` parameter would allow outside code to read its value without using the property getter. + +That's not the problem. C# fully permits passing a `readonly` field to an `out` parameter to a method call from within the constructor. It also allows passing a `readonly` field to a `ref` parameter as even without some form of initializer they are implicitly zeroed out. The situation here is more of semantics, the desire for read-only auto-properties to feel like they have a getter even though they don't. + +Perfectly legal C# 1.0-6.0 +```cs +public class Foo { + private readonly int x; + + public Foo(string s) { + int.TryParse(s, out x); + } +} +``` + +Apparently not legal in C# 6.0: +```cs +public class Foo { + private int X { get; } + + public Foo(string s) { + int.TryParse(s, out X); + } +} +``` + +Workaround: +```cs +public class Foo { + private int X { get; } + + public Foo(string s) { + int x; + int.TryParse(s, out x); + X = x; + } +} +``` + + +--- + +On Oct 4, 2014 at 3:31 PM @KathleenDollard wrote: + +Lucian, + +Will the ISymbol interface be marked with an attribute, and the analyzer look for that attribute? Or hard code to ISymbol? Or something else. + +As has been pointed out here, this isn't just a problem for you, but a problem we can have/are already having. + +I think this analyzer can probably be created in a generalized way which would have the enormous benefit of a single attribute. + +I'm not very worried about implementations. For both this (and protectedAndInternal (IL name)) I'm very, very, very, very, very worried about variations in the definition - the semantics. + +If you can help us get a SINGLE attribute for key concepts like these as they come up, that will be enormously helpful. + +Perhaps it is logical to have a second tier of "language suggestions" driven by the community finding common semantics that can drive adoption of non-MS analyzers and MS analyzers, spread the load for analyzers better. + +I'm good, even excited about the community solving problems. But we need common semantics - a single name for the "don't re-implement this interface, that'snot what it's for" message. + +Kathleen + +--- + +On Oct 4, 2014 at 3:45 PM @AdamSpeight2008 wrote: + +What about each aspect of ISymbol being split out into separate interfaces then use Constraint Generics on the method. + +``` +interface IValue< Vt > { .Value : Vt } +interface IKey< Kt > { .Key : Kt } + +someMethod< T : { IKey, IValue }> +``` + +--- + +On Oct 4, 2014 at 5:31 PM @nmgafter wrote: + +**KathleenDollard wrote:** +> Will the ISymbol interface be marked with an attribute, and the analyzer look for that attribute? Or hard code to ISymbol? Or something else. + +The approach I'm aiming for would be a new platform attribute + +```C# +namespace System.Runtime.CompilerServices +{ + /// + /// This attribute is applied to an interface to require that it may only be implemented inside + /// its own assembly, or inside assemblies to which InternalsVisibleTo access has been granted. + /// + public class InternalImplementationOnlyAttribute : Attribute { } +} +``` + +And an analyzer that comes with the platform that "enforces" this attribute with a diagnostic. + +--- + +On Oct 6, 2014 at 5:01 PM @KathleenDollard wrote: + +nmgafter. + + + +Kathleen + +--- + +On Oct 6, 2014 at 11:02 PM @supercat wrote: + +**nmgafter wrote:** +> The approach I'm aiming for would be a new platform attribute +> public class InternalImplementationOnlyAttribute : Attribute { } + +From what I understand, if the C# or VB.NET compiler would allow a interface to contain a method with a parameter of an internal type, the resulting interface would behave to the outside world as though that method didn't exist, but any type which couldn't see the definition of that parameter's type would be unable to implement that member, and thus unable to implement the interface as a whole. + +--- + +On Oct 19, 2014 at 7:56 AM @lwischik wrote: + +**supercat wrote:** +> An `out` parameter is really a `ref` parameter in disguise, except that the compiler is willing to ignore the fact that a variable passed as an `out` parameter is uninitialized. Interestingly, even when passing a variable as an `out` parameter to virtual method, the C# compiler does nothing to prevent an implementation written in another language from reading the previous contents of that variable (such as either clearing the variable before making the call, or creating a blank temporary variable and passing a `ref` to that). Passing the backing field of a class as an `out` parameter would allow outside code to read its value without using the property getter. + +The interop parts of "Out" weighed heavily on our minds. We came up with a design for Out parameters that we thought was good: +https://roslyn.codeplex.com/discussions/540509 section 42 + +Unfortunately this was one of the things that didn't meet the bar for VS14. + + +--- + diff --git a/meetings/2014/LDM-2014-10-08.md b/meetings/2014/LDM-2014-10-08.md new file mode 100644 index 0000000..2157faf --- /dev/null +++ b/meetings/2014/LDM-2014-10-08.md @@ -0,0 +1,213 @@ +# Back-compat break with interface implementations + +## High-level summary + +* _On the declaration side,_ C# users always used to be able to declare overloads of ambiguous interface methods. We are adding this ability also to VB for VS2015. +* _On the consumption side,_ C# language is more "hide-by-name" while VB supports both "hide-by-name" and "hide-by-sig". +* In such cases VB used to try to emulate the C# behavior when meta-importing such declarations. Now it will follow the more VB behavior. +* _This will cause some small breaking changes_. The breaking cases are limited to where... +* (1) Library code was authored in C# +* (2) In that library an interface ID inherits from two others IA,IB and declares a more general overload of a method in each of them +* (3) VB consumers of that library used to pick the more general overload (copying C#'s hide-by-name behavior). But now they will pick the more specific overload (using VB's hide-by-sig behavior) +* (4) If there were ambiguous specific overloads then it will be a break for VB consumers of the C# library. + + +## Background + +``` vb +Interface A + Sub M(x As Integer) +End Interface + +Interface B + Sub M(x As String) +End Interface + +Interface C : Inherits A,B + Overloads Sub M(x As Char) +End Interface + +Interface D : Inherits A, B +End Interface +``` + +DECLARATION: Declaration C" had been illegal in VB up to VS2013; you could only declare M in C with 'Shadows, or meta-import it from C#. But VB Roslyn made a change... it now lets you *declare* such things. + +CONSUMPTION: We never came up with a policy around CONSUMPTION of such things. + +* VS2013: In the type "C" if you try to access "M", the compiler doesn't see "M" from A and B. It only sees it from C. Note: you can meta-import C, but can't declare it. +* VS2013: In the type "D" if you try to access "M", the compiler gives a name-lookup ambiguity error prior to even attempting overload resolution. +* Roslyn as of May 2014: In both "C" and "D" if you try to access "M", the compiler gives name-lookup ambiguity error prior to even considering overload resolution. + +## Request + +We'd like to be able to invoke "M" in both "C" and "D". But we'd also like to preserve backwards compatibility. It's a conundrum. + + +Note that there's a similar issue with Modules: +``` vb +Module Module1 + Function Sin(d As Double) As Double + +Modle Module2 + Function Sin(s As Single) As Single + +' this callsite is in a place that imports both modules' containing namespaces +Dim x = Sin(1.0) ' VB currently makes this an ambiguity. +``` +VB makes this invocation of Sin an ambiguity. But when you do the equivalent in C# using "static using", it gathers together all candidates from all usings, and then performs overload resolution on them. This case seems very minor. Not worth worrying about. + +## VB LDM 2014.04.16 + +PROPOSAL1: revert change that enabled declaration in C. (but doesn't help consumption of meta-imported C, nor consumption of D). + +PROPOSAL2: silently treat it as shadowing at the callsite, consistent with native compiler. (but doesn't help consumption of D). + +_PROPOSAL3: Allow at lookup, i.e. merge them all, and leave it to overload resolution. (This is the proposal that we picked)._ This is a small breaking change. It will break code which used to meta-import "C", and invoked "M" in it, and always got the version of "M" in "C". Now with this change, it might get the version of "M" from "A" or "B". We think this is a minor breaking change, because (1) it only applied to meta-import situations, (2) it will now behave as the author intended. Note that this requires a change to VB spec 4.3.2. + +PROPOSAL4: combination of 1 and 2, i.e. completely duplicate behavior of native compiler. + +## VB LDM 2014.10.08 + +We revisited the previous conclusion in the light of implementation. The related bug numbers are: +https://roslyn.codeplex.com/workitem/34 +Internal vstfdevdiv bug 527642 + +The issue is that the C# language has hide-by-name lookup rules, and because of this it doesn't matter whether it meta-exports its signatures as "hide-by-name" or "hide-by-sig"; that difference is ignored. It happens to export them as "hide-by-sig". + + +Here's a representative example of the kind of breaks we'll now get under PROPOSAL3. There is a library that was authored in C#: +```cs +interface IA {void m(int x);} +interface IB {void m(int x);} +interface ID : IA,IB {void m(object x);} +``` +C# users of the library are able to use it fine: +```cs +ID d = null; +d.m(1); // picks the "object" overload +``` +However VB users of the library will now experience a break +```vb +Dim x As ID = Nothing +x.m(1) ' VS2013 used to pick 'Object' but under Proposal3 will now give an ambiguity error +x.m(CObj(1)) ' this is the workaround to use in this case +``` + +The breaking cases are limited to where C# had ambiguous methods in unrelated interfaces, and overrode them with a more general overload in the derived interface ID. We imagine this breaking case will be uncommon. + + + +There's a more common related scenario which WILL CONTINUE to work just fine. Let's walk through it: +```cs +interface IJson {string GetName();} +interface IComponent {string GetName();} +interface IPatient : IJson,IComponent {} +``` +I am a C# author. I am taking in two libraries that provide IJson and IComponent. They are unrelated by they happen to have a method which shares a signature and has more or less the same behavior. I write it as above. However, within my C# use of my library, I discover a problem straight away: `IPatient y = ...; y.GetName();` This code will give a compile-time ambiguity error because it doesn't know which unrelated overload of GetName to use. So I fix up my C# library as follows, to silence up the compiler: +```cs +interface IPatient : IJson,IComponent {new string GetName();} +``` +With this change, the same C# code `IPatient y = ...; y.GetName();` will work just fine. The reason it works in C# is because C# language uses hide-by-name lookup rules, and finds IPatient.GetName, and is happy with that. +```vb +Dim y As IPatient = Nothing +y.GetName() ' correctly picks IPatient.GetName +``` +Fortunately the VB consumers of the library will also work great. In VS2013 it worked because VB faked up hide-by-name semantics on meta-import and so got the derived class. Under Proposal3 it will work because VB honestly uses hide-by-sig semantics and in this case (unlike ID.m(object) above) the hide-by-sig semantics pick the correct method. + + +How did VS2013 VB fake up "hide-by-name" semantics? Well, upon meta-import, it did a "shadowing trick": if it encountered an interface like C, it knew that such an interface could not have been declared by VB, and therefore it must have been declared in C#, and C# language had hide-by-name (shadowing) semantics, and so VB ignored the "hide-by-sig" attribute on the metadata and imported it as hide-by-name. + + +We went back on our resolution. We decided to forbid declaration of interfaces like "C". By forbidding it, we open the door to pulling the same "shadowing" trick as VS2013 did on meta-import. Actually the Roslyn compilers have a cleaner design and can't pull the trick on meta-import; however they can pull it on name lookup. + +In other words, we resolved to go for PROPOSAL4. + +## VB LDM 2014.10.08 + +Gosh this is difficult. In trying to replicate the behavior of VS2013, Aleksey discovered some pretty weird bugs in the way VS2013 applied its shadowing heuristics. + +```vb +' BUG NUMBER 1 IN VS2013 + +Interface IA(Of T) + Sub M1(x As T) +End Interface + +Interface IB + Inherits IA(Of Integer), IA(Of Short) + Overloads Sub M1(x As String) ' VS2013 doesn't report an error +End Interface + +Interface IC1 + Sub M1(x As Integer) +End Interface + +Interface IC2 + Sub M1(x As Short) +End Interface + +Interface ID + Inherits IC1, IC2 + Overloads Sub M1(x As String) ' VS2013 reports error BC31410 "Overloading methods declared in multiple base interfaces is not valid" +End Interface + +Module Module2 + Sub Test(x As IB) + x.M1(1) ' VS2013 reports error BC30685: 'M1' is ambiguous across the inherited interfaces 'IA(Of Integer)' and 'IA(Of Short)' + End Sub + + Sub Test(x As ID) + x.M1(1) 'ID + End Sub +End Module +``` +What's strange in this bug number 1 is that you expect the declaration error BC31410 to be reported in both cases IB and ID. However VS2013 only reports a declaration error for ID. Instead it reports a consumption error for IB. + +```vb +' BUG NUMBER 2 IN VS2013 + +Interface IA + Sub M1() +End Interface + +Interface IB + Inherits IA + Overloads Sub M1(x As Integer) +End Interface + +Interface IC1 + Inherits IA, IB + Overloads Sub M1(x As String) ' VS2013 reports error BC31410 "Overloading methods declared in multiple base interfaces is not valid." +End Interface + +Interface IC2 + Inherits IB, IA + Overloads Sub M1(x As String) ' VS2013 reports error BC31410 "Overloading methods declared in multiple base interfaces is not valid." +End Interface + +Interface IC3 + Inherits IB + Overloads Sub M1(x As String) ' VS2013 reports no error +End Interface +``` +What's strange about this is that interfaces IC1, IC2 and IC3 are semantically equivalent: whether or not you explicitly declare that you inherit from IA is irrelevant, since it's implied. So the compiler shouldn't be reporting a declaration error in IC1/IC2, just as it doesn't report a declaration error in IC3. + +This means that we have to refine our idea of Proposal 4: + +PROPOSAL4a: disallow declaration C by reimplementing all the weird quirks of VS2013, and use implicit shadowing upon name lookup. We rejected this as too difficult: Roslyn has very different code paths, so there's a high risk that we'd get different behavior from VS2013. + +PROPOSAL4b: disallow declaration C in a clean way without any of the weird quirks of VS2013. + +The question is: out of Proposal3 and Proposal4b, which will be the most painful break? + +Proposal4b will hurt people who declare things that used to be okay under the old quirky rules but are no longer okay. We haven't discovered many cases yet. + +Proposal3, as we said before, will hurt people who invoked x.m(1) in the case identified above. Their workaround is to change the callsite with an explicit cast. There is precedent for this: the overload resolution rules between VB and C# are different, and VB people have to put explicit casts in other callsites. + +COM is an interesting case where you generally have IFoo1 and IFoo2 where IFoo2 duplicates (doesn't inherit) the methods of IFoo but also adds a few others. However we don't see this as a problem. It's very nontypical to declare a third interface IBar which inherits both IFoo1 and IFoo2 in COM scenarios. + +In the light of this new evidence, we prefer Proposal3. It will be a better cleaner future for the VB language to declare things like this. And it puts the pain on callsites to ambiguous interfaces, rather than producers of them; it's generally easier to fix callsites. As for the breaking changes, some of them will be silently picking more appropriate overloads, which is generally the right thing to do and more intuitive. Only in rare and contrived cases (where C# author overrides two ambiguous specific methods with a less specific methods) will there be a compile-time failure. + +Note that the VS2015 CTP4 (already shipped by the time we had this LDM) lets you declare things like "C" but there's not even any overload resolution at the callsite. That will give us lots of opportunity to hear from customers whether they're suffering from the fact that we no longer do implicit shadowing. If we hear pain, then we can re-visit. + diff --git a/meetings/2014/LDM-2014-10-15.md b/meetings/2014/LDM-2014-10-15.md new file mode 100644 index 0000000..2688185 --- /dev/null +++ b/meetings/2014/LDM-2014-10-15.md @@ -0,0 +1,930 @@ +We've been reconsidering some details about the "nameof" operator. This was prompted by a few questions: + +* _Why can't I write "nameof(this.p)"? My convention is to always put 'this' in front of my fields and properties._ +* _Why does the IDE throw an exception when I rename symbols that appear in nameof expressions? Why is it so hard to write analyzers that work correctly in the face of nameof?_ + +A revised spec is below. The chief difference from the original spec ([part1](https://roslyn.codeplex.com/discussions/552376) and [part2](https://roslyn.codeplex.com/discussions/552377)) is that now the argument of nameof(.) is just an expression, like any other expression in the language (in CTP4 it had been an unusual hybrid similar in some ways to the argument of typeof). The common cases of nameof will still be written the same as what's in CTP4, but some edge cases will be different. + +There's one chief open design question remaining, detailed below. + +Please let us know what you think! + + +# nameof operator: revised spec + +The nameof(.) operator takes one expression argument (note that in C#, types are expressions). There are two principles: in essence, (1) the expression must "have a name", and (2) it must resolve to one single symbol. + +## Bread and butter cases +```cs +// Validate parameters +void f(string s) { + if (s == null) throw new ArgumentNullException(nameof(s)); +} +``` + +```cs +// MVC Action links +<%= Html.ActionLink("Sign up", + nameof(UserController), + nameof(default(UserController).SignUp())) +%> +``` + +```cs +// INotifyPropertyChanged +int p { + get { return this._p; } + set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); } +} +``` + +```cs +// XAML dependency property +public static DependencyProperty AgeProperty = DependencyProperty.Register(nameof(default(C).Age), typeof(int), typeof(C)); +``` + +```cs +// Logging +void f(int i) { + Log(nameof(f(i)), "method entry"); +} +``` + +```cs +// Attributes +[DebuggerDisplay("={" + nameof(default(C).getString()) + "()}")] +class C { + string getString() { ... } +} +``` + +_I'm sorry. Markdown keeps turning my plus symbols into ampersand hash 43. I don't know how to stop it._ + +I call out the last two cases. If we decided to break principle (2), then we could allow method-groups, and hence omit the argument lists for these two cases. We could reasonably choose either to keep principle (2) or to break it, and there are pros and cons to both sides. More details in the post below. + +# Implementation +```cs +class NameOfExpressionSyntax : ExpressionSyntax { readonly ExpressionSyntax argument; } +``` +For source code that compiles without errors, semanticModel.GetSymbolInfo(argument) will return a single symbol. For erroneous code it might also return 0 or many candidates. + +(Under the alternative option, where principle (2) is violated, then we would make GetSymbolInfo return no single preferred symbol, and instead always return a candidate list. Analyzers would now have to deal with this case for successful user code as well as for erroneous user code.) + +# C# Syntax +``` +expression: … | nameof-expression + +nameof-expression: + nameof ( nameof-expression-argument ) + +nameof-expression-argument: + simple-name x + member-access e.x, e.x, e?.x, + int.TryParse, a::b.c + invocation-expression e(args) + base-access-named base.x +``` + +It is helpful to list what is not allowed as the nameof argument. In general, nameof accepts expressions that have names (including type expressions that have names). All other arguments produce compile-time error "This expression does not have a name". Here are the expressions that are not allowed: +``` + assignment x += 15 + query-expression from y in z select y + lambda-expression () => e + conditional-expression a ? b : c + null-coalescing-expression a?? b + binary-expression ||, &&, |, ^, &, ==, !=, + <, >, <=, >=, is, as, <<, + >>, +, -, *, /, % + prefix-expression +, -, !, ~, ++, --, + (cast)e, *, & + postfix-expression ++, -- + array-creation-expression new C[…] + object-creation-expression new C(…) + delegate-creation-expression new Action(…) + anonymous-object-creation-expression new {…} + typeof-expression typeof(int) + checked-expression checked(…) + unchecked-expression unchecked(…) + default-value-expression default(…) + anonymous-method-expression delegate {…} + pointer-member-access e->x + sizeof-expression sizeof(int) + literal "hello", 15 + parenthesized-expression (x) + element-access e[i] + this-access this + base-access-indexed base[i] + await-expression await e + nameof-expression nameof(e) + vb-dictionary-lookup e!foo +``` + +Note that there are some types which are not counted as expressions by the C# grammar. These are not allowed as nameof arguments (since the nameof syntax only allows expressions for its argument). It is a pleasant coincidence that none of these types even have names, so they wouldn't be useful as a nameof argument anyway. Even keywords like "int" and "bool" are not names; they are keywords. There's no need to spell out that the following things are not valid expressions, since that's already said by the language syntax, but I'm going to spell it out anyway. +``` + predefined-type int, bool, float, object, + dynamic, string, void + nullable-type Customer? + array-type Customer[,] + pointer-type Buffer*, void* +``` + +# Semantics + +The nameof expression is a constant. In all cases, nameof(…) is evaluated at compile-time to produce a string. Its argument is not evaluated at runtime, and is considered unreachable code (however it does not emit an "unreachable code" warning). + +_Name lookup._ The same rules of "simple name lookup" and "member access" apply to nameof arguments as they do to the rest of the language. + +_Accessibility._ The same rules of accessibility apply to nameof arguments as they do to all other expressions. + +_Error in binding._ The same rules of binding apply to nameof as to all other expressions. If binding to the argument/type would result in an error, e.g. to obsolete methods, then that is still an error for purposes of nameof. + +_Definite assignment._ The same rules of definite assignment apply to nameof arguments as they do to all other unreachable expressions. + +_VB invocation / indexing._ In VB, e(…) might be either an method invocation, a delegate invocation, an array indexing, a property access, a default property access, or an invocation of a parameterless function "e" followed by indexing. Which one of these it is, is determined by symbol resolution. After symbol resolution, as with C#, only method invocations are allowed as nameof arguments. + +_Result of the nameof operator._ This depends on the form of the nameof argument… + +__nameof(simple-name)__, of the form I +__nameof(member-access)__, of the form E.I +__nameof(base-access-named)__, of the form base.I + +These cases are all resolved using the rules for simple name lookup $7.6.2 or member access $7.6.4. If they succeed in binding, they must bind to one of: + +* A method-group. This produces an error "To specify the name of a method, you must provide its arguments". +* A variable, value, parameter, constant, enumeration-member, property-access, field, event, type-parameter, namespace or type. In this case the result of the nameof operator is simply "I", which is generally the name of the symbol that the argument bound to. There are some caveats… + +If "I" identified an alias e.g. "using I = X.Y.Z; nameof(I)", then the result of nameof is still "I", even though the expression bound to the type X.Y.Z. + +Also "I" undergoes the standard identifier transformations prior to being returned from nameof. In C# these are detailed in $2.4.2 of the C# spec: first any leading @ is removed, then Unicode escape sequences are transformed, and then any formatting-characters are removed. This of course still happens at compile-time. In VB, any surrounding [] is removed. + +Also in VB the result has the capitalization that was used in the argument. This might be different from the capitalization of the symbol that the argument bound to. + + +__nameof(invocation-expression)__, of the form F(args) + +The invocation expression must be either a method-invocation because F was a method-group, or a delegate-invocation because F was a value of delegate type. + +* If a method-invocation, then F must have had the form E.I or I or base.I from other rules in the language, and must have bound to a method-group. F(args) must bind to a unique method by the normal rules of overload resolution. The result of the nameof operator is simply I, subject to the same transformations as above. + +* If a delegate-invocation, it is a compile-time error "This expression does not have a name." + +# Examples + +```cs +void f(int x) { + nameof(x) +} +// result "x": Parameter (simple name lookup) +``` + +```cs +int x=2; nameof(x) +// result "x": Local (simple name lookup) +``` + +```cs +const x=2; nameof(x) +// result "x": Constant (simple name lookup) +``` + +```cs +class C { + int x; + … nameof(x) +} +// result "x": Field (simple name lookup) +``` + +```cs +class C { + void f() {} + … nameof(f) +} +// result: error "To specify the name of a method, you must provide its arguments": Method-group (simple name lookup) +``` + +```cs +class C { + void f() {} + … nameof(f()) +} +// result "f": Invocation-expression that resolves successfully +``` + +```cs +class C { + void f(){} + void f(int i){} + … nameof(f(1)) +} +// result "f": Invocation-expression that resolves successfully +``` + +```cs +Customer c; … nameof(c.Age) +// result "Age": Field (member access) +``` + +```cs +Customer c; … nameof(c._Age) +// result error "_Age is inaccessible due to its protection level: Private field (member access) +``` + +```cs +nameof(Tuple.Create) +// result error "To specify the name of a method, you must provide its arguments": Method-group (member access) +``` + +```cs +nameof(Tuple.Create(1,2)) +// result "Create": Invocation-expression that resolves successfully, including with generic type inference +``` + +```cs +nameof(System.Exception) +// result "Exception": Type (member access) +``` + +```cs +nameof(List) +// result "List": Type (simple name lookup) +``` + +```cs +nameof(List<>) +// result error "type expected": Unbound types are not valid expressions +``` + +```cs +nameof(List.Length) +// result error "List doesn't contain a member Length": Member access, is unable to find an static member named Length on this type +``` + +```cs +nameof(default(List)) +// result error "This expression doesn't have a name": Not one of the allowed forms of nameof +``` + +```cs +nameof(default(List).Length) +// result "Length": Property access (member access) +``` + +```cs +nameof(int) +// result error "Invalid expression term 'int'": Not an expression. Note that 'int' is a keyword, not a name. +``` + +```cs +nameof(System.Int32) +// result "Int32": Type (member access) +``` + +```cs +using foo=System.Int32; +nameof(foo) +// result "foo": Alias (simple name lookup) +``` + +```cs +nameof(System.Globalization) +// result "Globalization": Namespace (member access) +``` + +```cs +nameof(x[2]) +nameof("hello") +nameof(1+2) +// error "This expression does not have a name": Not one of the allowed forms of nameof +``` + +```vb +NameOf(a!Foo) +' error "This expression does not have a name": VB-specific. Not one of the allowed forms of NameOf. +``` + +```vb +NameOf(dict("Foo")) +' error "This expression does not have a name": VB-specific. This is a default property access, which is not one of the allowed forms. +``` + +```vb +NameOf(dict.Item("Foo")) +' error "This expression does not have a name": VB-specific. This is an index of a property, which is not one of the allowed forms. +``` + +```vb +NameOf(arr(2)) +' error "This expression does not have a name": VB-specific. This is an array element index, which is not one of the allowed forms. +``` + +```vb +Dim x = Nothing +NameOf(x.ToString(2)) +' error "This expression does not have a name": VB-specific. This resolves to .ToString()(2), which is not one of the allowed forms. +``` + +```vb +Dim o = Nothing +Dim b As Func(Of Object, Object, Boolean) = AddressOf o.Equals +' result "Equals". Warning "Access of static member of instance; instance will not be evaluated": VB-specific. VB allows access to static members off instances, but emits a warning. +``` + +```cs +[Foo(nameof(C))] +class C {} +// result "C": Nameof works fine in attributes, using the normal name lookup rules. +``` + +```cs +[Foo(nameof(T))] +class C {} +// result error "T is not defined": A class type parameter is not in scope in an attribute on that class +``` + +```cs +[Foo(nameof(T))] void f { } +// result error "T not defined": A method type parameter is not in scope in an attribute on that method +``` + +```cs +void f([Attr(nameof(x))] int x) {} +// result error "x is not defined": A parameter is not in scope in an attribute on that parameter, or any parameter in the method +``` + +```vb +Function f() + nameof(f) +End Function +' result "f": VB-specific. This is resolved as an expression which binds to the implicit function return variable +``` + +```vb +NameOf(New) +' result error "this expression does not have a name": VB-specific. Not one of the allowed forms of nameof. Note that New is not a name; it is a keyword used for construction. +``` + +```vb +Class C + Dim x As Integer + Dim s As String = NameOf(x) +End Class +' result "x": VB-specific. Field access (simple name lookup) +``` + +```cs +class C { + int x; + string s = nameof(x); +} +// result error "cannot reference non-static field": C#-specific. Normal name referencing error +``` + +```cs +class C { + int x; + string s = nameof(C.x); +} +// error "C doesn't contain a member named x": Normal member access rules, with static/instance mismatch +``` + +```cs +class C { + int x; + string s = nameof(default(C).x); +} +// result "x": This is the C# idiom for getting the name of an instance variable. +``` + +```cs +struct S { + int x; + S() {var s = nameof(x); …} +} +// result "x": Field access (simple name lookup). Nameof argument is considered unreachable, and so this doesn't violate definite assignment. +``` + +```cs +int x; … nameof(x); x=1; +// result "x": Local access (simple name lookup). Nameof argument is unreachable, and so this doesn't violate definite assignment. +``` + +```cs +int x; nameof(f(ref x)); +// result "f": Invocation expression. Nameof argument is unreachable and so can be used as a ref argument prior to definite assignment. +``` + +```cs +var @int=5; nameof(@int) +// result "int": C#-specific. Local (simple name lookup). The leading @ is removed. +``` + +```cs +nameof(m\u200c\u0065) +// result "me": C#-specific. The Unicode escapes are first resolved, and the formatting character \u200c is removed. +``` + +```vb +Dim [Sub]=5 : NameOf([int]) +' result "int": VB-specific. Local (simple name lookup). The surrounding [.] is removed. +``` + +# Chief Open Design Question + +Principle (2) says the nameof argument must resolve to one single symbol. Is this a principle worth sticking to? It could go either way. Let's examine the issues, starting with some concrete examples... + +```cs +// Logging +void f(int i) { + Log(nameof(f(i)), "method entry"); +} +``` + +```cs +// Attributes +[DebuggerDisplay("={" + nameof(default(C).getString()) + "()}")] +class C { + string getString() { ... } +} +``` + +If we decided to break principle (2), then we could allow method-groups, and hence omit the argument lists for these two examples. We could reasonably choose either to keep principle (2) or to break it, and there are pros and cons to both sides. + +## Comparison: code +```cs +void M(int i) { +} +void M(string s) { + var x = nameof(M); // error with principle (2); okay without it + var x = nameof(M(s)); // okay + var x = nameof(M(s)); // okay +} +``` + +## Comparison: logging code +```cs +// Logging +void f(int i) { + Log(nameof(f(i)), "method entry"); // with principle (2) you have to supply arguments + Log(nameof(f), "method entry"); // without it, you can omit arguments +} +``` + +## Comparison: attributes +```cs +// Attributes +[DebuggerDisplay(nameof(default(C).getString())] // with principle (2) you supply parentheses +[DebuggerDisplay(nameof(default(C).getString)] // without it, you can omit them +class C { + string getString() { ... } +} +``` + +## Comparison: IDE behavior + +With principle (2), IDE refactoring behavior will be understandable, e.g. if you rename M then you can anticipate how it will be renamed inside nameof arguments. It's easy to understand+predict what HighlightAllReferences and GoToDef and FindAllReferences will do. + +Without principle (2), IDE refactoring becomes less predictable. What will happen if you rename-refactor the second "M" in _Comparison:Code_? Would it rename the first one as well, and also the nameof argument? It's also not clear how CodeLens and other tools would count nameof(M). Would it count it as a reference to all overloads of M? There are probably similar issues all over the IDE. + +## Comparison: typing burden + +With principle (2), you always need to specify which overload of M you're referring to, even though they have the same name. (In mitigation, it's often quite easy to specify which overload. As in the logging case, you typically do it within the method you wish to name, and so you typically have parameters to hand). + +## Comparison: conceptual burden + +Without principle (2), nameof will be the _only_ part of the language where "method-group" is a valid expression. Everywhere else it must be resolved to a single method. + +## Comparison: analyzer burden + +With principle (2), it will be easier to write analyzers. They won't blow up in untested corner cases involving nameof. + +--- +--- + +On Oct 18, 2014 at 10:38 AM @BachratyGergely wrote: + +This is getting awfully close to the `infoof` operator. Should we start holding our breath? :) + +Did you consider allowing method groups only if it is not overloaded? +Pro: less typing. Most of the time methods don't have overloads. +Con: introducing an overload breaks the build. + +Could you explain why you disallow indexers altogether? An indexed property does have an associated metadata entry with a name (usually `Item` or defined by `IndexerNameAttribute`), which sometimes occurs in PropertyChanged, e.g. WPF expects `Item[]` when the indexer property changes for some index value. It's not that big of a deal since we don't write custom collections implementing `INotifyPropertyChanged` all day long but I'd still like to know for the sake of completeness. + +``` C# +var x = nameof(M(i)); // okay +``` +There's no `i` in scope. Shouldn't this be `M(s)`? + +``` C# +using I = X.Y.Z; nameof(I) +``` +I think this should produce the same value as `nameof(X.Y.Z)` for consistency with `typeof(I)` and practically every other use case of an alias. An alias is just a compile-time artifact whereas pretty much all other expressions above have an associated IL metadata entry. We should be able to replace the alias with the concrete type without any semantic changes to the program. + + +--- + +On Oct 18, 2014 at 12:12 PM @agat50 wrote: + +Hmm. I don't understand why we should call nameof(Func1(i)) instead of nameof(Func1). Even if there is else one Func1(double), for example, - name will be same Func1 cause it's not infoof design with exact func description. How about + +``` c# +public void Func1(int a){} +public void Func1(string s){} +var func1Name = nameof(Func1(i)); // ??? +var func1Name1 = nameof(Func1(default(string)); //Seems resolved + +public void Func2(int a, int b){} +public void Func2(int a, T1 b, bool b1 = false) where T1 : class +{} +var func2Name = nameof(Func2(i,j)); // ??? +var func2Name2 = nameof(Func2(i,null)); //Ok, resolved, what about + +public void Func3(int a, T1 b) where T1 : IDisposable +{} +var func3Name = nameof(Func3(default(int),default(IDisposable))); // Is it correct? + +double i = 0.0; +var func1Name = nameof(Func1(i)); // ??? name conflict +``` +cases? + +**Added. Seems like default() resolving all issues, no need for removing (2) principle. + +--- + +On Oct 18, 2014 at 2:15 PM @Halo_Four wrote: + +**agat50 wrote:** +> Hmm. I don't understand why we should call nameof(Func1(i)) instead of nameof(Func1). Even if there is else one Func1(double), for example, - name will be same Func1 cause it's not infoof design with exact func description. + +They would both be "Func1", but if you decided to refactor the name of one of those Func1 functions the IDE would have no idea whether or not you would also want to change the name used in the `nameof()` clause. + +**to lwischik:** +Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use `default()` everywhere. + +```cs +int x = 123; +var func3Name = nameof(Func3(x, IDisposable)); +``` + +As already mentioned, overload resolution was the big thing holding back the concept of an `infoof()` clause, if those concerns must be solved now could there still be time to implement that feature? + +And I'm a little confused as to some of the restrictions. Why wouldn't `nameof(int)` be just as valid as `nameof(foo)` in that they're both effectively aliases of `System.Int32`? And why would `nameof(System.Int32)` be considered member access? Is that just for the sake of parsing? + +--- + +On Oct 18, 2014 at 4:34 PM @BachratyGergely wrote: + +**Halo_Four wrote:** +> **to lwischik:** +> Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use `default()` everywhere. +> +> ```cs +> int x = 123; +> var func3Name = nameof(Func3(x, IDisposable)); +> ``` + +Probably to keep it simple. `nameof` accepts a strict subset of completely valid C# expressions, the parser is already in place to handle these. To allow type names in those expressions you would +* introduce constructs that are conceptionally new +* need to determine that such constructs do not introduce ambiguity +* write the parser. We're messing with overload resolution here. That's one of the more complex parts in the compiler. +* test, etc. +That seems an awful lot of work compared to the gain. Even though `nameof(Func(default(A), default(B), default(C))` feels odd I'd still be satisfied with this tradeoff. + +--- + +On Oct 18, 2014 at 5:01 PM @Halo_Four wrote: + +**BachratyGergely wrote:** +> **Halo_Four wrote:** +> > **to lwischik:** +> > Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use `default()` everywhere. +> > +> > ```cs +> > int x = 123; +> > var func3Name = nameof(Func3(x, IDisposable)); +> > ``` +> +> Probably to keep it simple. `nameof` accepts a strict subset of completely valid C# expressions, the parser is already in place to handle these. To allow type names in those expressions you would +> * introduce constructs that are conceptionally new +> * need to determine that such constructs do not introduce ambiguity +> * write the parser. We're messing with overload resolution here. That's one of the more complex parts in the compiler. +> * test, etc. +> That seems an awful lot of work compared to the gain. Even though `nameof(Func(default(A), default(B), default(C))` feels odd I'd still be satisfied with this tradeoff. + +Yeah, I understand. At this point in the game nobody wants to be introducing more work to an already complex part of the compiler. And if this is just a stepping point into such operators then we can live with syntax oddity for the time being and maybe it can get some additional polish in C#vNext, especially if it leads to an additional set of operators like `infoof()` or `memberof()`. + +Also, I double checked the above and while `int` is effectively treated like an alias to `System.Int32` it is a keyword and cannot be re-aliased like another alias could be, e.g.: + +```cs +using foo = int; // compiler error, int is a keyword +using bar = System.Int32; // fine +using baz = bar; // also fine +``` + +Just another one of those parser oddities that make something like `nameof()` feel half-baked, but it's understandable at least for now. + +--- + +On Oct 18, 2014 at 5:08 PM @simonlbc wrote: + +I was wring the following when I read Halo_Four's post: + + + +> I find the fact that you can use a method's parameters as part of the nameof body to be a little + +> In order to explicitly choose the right simbol, why not go: + + +> ```cs +> // Logging +> void f(int i) { +> Log(nameof(f(int)), "method entry"); +> } +> ``` + +> This way, you won't get developers doing something like ```"".Substring(44)``` (unless that wouldn't be a valid nameof body) with values that don't make sense and that doesn't mean anything aside from choosing a type for the parameter. I do agree it would probably need an whole new handling of "method calls" instead of re-using the language's syntax. + +I therefor ask the same question as he is :) + +--- + +On Oct 18, 2014 at 5:58 PM @Halo_Four wrote: + +To add a little more context to the conversation: + +[Eric Lippert's Blog: In Foof We Trust](http://blogs.msdn.com/b/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx) + +A good rundown on how complicated using types for overload resolution might be. + +Eric's example obviously demonstrates problematic ambiguity, but the compiler already handles that ambiguity now with normal overload resolution. That ambiguity is also a bizarre edge-case which I don't think represents a real world scenario. After all, the C# compiler won't let you call the generic versions of the overloaded method without reflection, to my knowledge, so why not also require the programmer to break out the reflection if they want a MemberInfo for one or the other generic methods in this particularly odd case? + +CIL has obviously solved this problem since a similar syntax is supported by `ldtoken`, how does C# appreciably differ? + +My limited understanding of the parser as it stands today is that for overload resolution that it parses the expression for each argument, then infers their type, then compares the types to the possible method overloads. Does it change appreciably if the type inference is done by the programmer? + +All that aside I understand that this may be more a question of time than of anything else. + +--- + +On Oct 19, 2014 at 4:51 AM @lwischik wrote: + +# Comparisons +There are three good options on the table… + +``` +OPTION 0 +nameof-expression: nameof ( [type-or-namespace .] identifier ) +``` +Meaning of Option0: this identifier must resolve to one or more instance or static (or maybe also extension members?) on that type/namespace. Or, if it's an identifier on its own, it can also resolve to variables, parameters, constants, or to 1 or more members of the current or base classes. Accessibility is mostly respected except that "protected" has to be treated a bit differently. + +``` +OPTION 1 +nameof-expression: nameof ( expression ) +``` +Meaning of Option1: this expression must bind to either a named symbol or a method-group (i.e. several methods) + +``` +OPTION 2 +nameof-expression: nameof ( expression ) +``` +Meaning of Option2: this expression must bind to a single named symbol + + + +# Comparison: IDE HighlightSymbol, GoToDef, RenameRefactoring behavior + +The only reason that nameof even exists is to improve IDE behavior, to make renaming less error-prone. Let's see how it works with the different options: +```cs +class C { + void f(int i) { + nameof(f) // option 0/1 + nameof(f(i)) // option 2 + } + static void f(string s) { } +} + +static class Extensions { + static void f(this C c, double d) { } +} +``` +__HighlightSymbol__: when you set your cursor on the argument to nameof, options 0/1 would highlight all three declarations of f (instance, static and extension). Option 2 would only highlight the first declaration of f. + +__GoToDef__: when you do GoToDef on the argument to nameof, options 0/1 would presumably have to pop up the "find references" window for you to select which one you want to go to (or give an error). Option 2 would go straight to the first declaration. + +__Rename declaration__: when you rename the first declaration of f, options 0/1 would presumably leave the argument to nameof unchanged since it doesn't know whether nameof was referring to the first declaration of f or one of the other ones. That kind of defeats the whole point of nameof (which was to make renaming less error prone). Or maybe they would remain everything that was in the user's source code -- all three declarations of "f", and all nameof references to them. Option 2 would rename just the argument to nameof according to the renamed method declaration. + +__Rename argument__: when you rename the argument to f, options 0/1 would presumably pop up an error dialog telling you they're unable to rename it; either that or they'd rename all three declarations of f. Option 2 would rename only the first declaration. + + +#Comparison: IDE inline rename cleverness? + +The IDE "smart rename" functionality will be used by most analyzers to figure out the best way to qualify a given name. Here's an example. Start with this code: +```cs +class Agent { + int p; + void f(int _p) { + p = _p; + nameof(p); + } +} +``` +When you rename the method parameter to just "p", how does the inline rename avoid name-clashes? Like this: +```cs +class Agent { + int p; + void f(int p) { + this.p = p; + var s1 = nameof(Agent<,>.p); // option 0 + var s2 = nameof(this.p); // option 1/2 + } +} +``` +The question is, do you prefer `Agent<,>.p` or `this.p` ? + +# Comparison: CodeLens +How many times do you think each method is referenced? +```cs +// OPTION 0/1: +class C { + [3 references] void f(int i) { Log(nameof(f), i); } + [3 references] void f(string s) { Log(nameof(f), s); } + [3 references] void f(object o) { Log(nameof(f), o); } +} + +// OPTION 2: +class C { + [1 references] void f(int i) { Log(nameof(f(i)), i); } + [1 references] void f(string s) { Log(nameof(f(s)), s); } + [1 references] void f(object o) { Log(nameof(f(o)), o); } +} +``` +I think options 0/1 are quite odd. Is each method really referenced three times? I think each method is referenced only once. + +# Comparison: code for this.p +My team convention is to always write "this" explicitly. What do I write inside nameof? +```cs +class Agent { + int p; + void f() { + var y = this.p; + nameof(Agent<,>.p); // option 0 + nameof(this.p); // options 1/2 + } +} +``` + +# Comparison: code for instance members +```cs +var x = GetAgent(); +Log("{0} = {1}", nameof(Agent<,>.Length), x.Length); // option 0 +Log("{0} = {1}", nameof(x.Length), x.Length); // option 1/2 +``` +Similar to "this.p", options 1/2 are easier all around when naming members of instances that I have in hand. + +# Comparison: code for attributes on the same class +```cs +[DebuggerDisplay(nameof(getString))] // option 0 +[DebuggerDisplay(nameof(default(Agent).getString))] // option 1 +[DebuggerDisplay(nameof(default(Agent).getString()))] // option 2 +class Agent { + string getString() { … } +} +``` +Where option 0 shines is when you don't have an instance in hand. + +# Comparison: code for attributes on a different class +```cs +[DependsOn(nameof(Agent<,>.getString))] // option 0 +[DependsOn(nameof(default(Agent).getString()))] // option 1 +[DependsOn(nameof(default(Agent).getString))] // option 2 +class Peer { } +``` +Actually, all three options are impossible in this scenario unless the method is public. Assuming it is public, again open 0 is better when I don't have an instance in hand, because (1) I don't need to provide concrete type arguments, and (2) I don't need the "default" keyword. + +# Comparison: code for name of current function +```cs +void f(int i) { + nameof(f) // options 0/1 + nameof(f(i)) // option 2 +} +``` +Actually, this scenario is pretty unimportant. There's a much nicer way to get the name of the current member: [CallerMemberName]. + +# Comparison: code for linq members +```cs +var best = (from a in agents orderby a.Cost select new { a.Name, a.Cost }).First(); +Log("Cost = {0}", best.Cost); // impossible to use nameof with option 0 +Log("{0} = {1}", nameof(best.Cost), best.Cost); // option 1/2 +``` +This is a pretty niche scenario. Option 0 only works for members of nameable types. + + +--- + +On Oct 19, 2014 at 10:46 AM @JanKucera wrote: + +The ```nameof``` I was looking for does not use parameters for methods, that is, I prefer option 0/1. + +__HighlightSymbol__ and __GoToDef__ are fine for me. __Rename declaration__ used to have a dialog box where you could check which ones do you want remove you know... anyway, if it is all or nothing, I would prefer all. __Rename argument__: if I understand correctly, the issue is renaming the argument _i_ in function declaration to match the function name _f_ and introduce conflict at the context of ```nameof```. In that case, I prefer error to renaming things I didn't want to rename. + +All of _IDE inline rename cleverness?_, _code for this.p_ and _code for instance members_ makes me prefer option __1__. + +_Comparison: CodeLens_ are all strange for me. I would not consider the use in ```nameof``` to count towards references actually. + +On the other hand, _code for attributes on the same class_ makes me prefer option __0__. In other words, I want to avoid using type qualifier wherever possible. It is obviously inevitable in the case of _code for attributes on a different class_ but still, it is preferred. Making an instance of something just for ```nameof``` is a horrible syntax, especially when the semantics are making __null__ and using its members - that would actually reasonably count to 'reference'. +(btw. options 1/2 are swapped in that example I believe) + +I do not understand why it would be impossible to use option 0 in _code for linq members_. If the result is "Cost", how does it matter that type does not have a name? + + + +--- + +On Oct 19, 2014 at 2:47 PM @KathleenDollard wrote: + +The more I hear about this, the more strongly I feel you should do Option 2 now. + +Option 2 should always work. + +If you do Option 2, can you also later add Option 1 or 0? + +Perhaps Option 1 or 0 should be done later. The impact of the ambiguity is quite significant and each of the affected scenarios deserves significant thought. + +I do not yet like Option 0, but this approach allows time for an extensive conversation regarding Options 0 and 1. + +I think if so much had not already been cancelled you might consider cancelling this because of the late date. Of course no one really wants that, but I do think the late date means the most conservative approach is the best approach. I am defining late as not having very many public cycles left for comment. + +--- + +On Oct 20, 2014 at 12:30 PM @BachratyGergely wrote: + +Option 2 seems the most straightforward to me. It would also be a trivial upgrade path from poor man's nameof: +``` C# +private static string NameOf(Expression> expr) +{ + // Some System.Linq.Expressions magic to get the member name of the outermost expression +} +// before +string name = ExpressionUtil.NameOf(() => some.Expression(a,b)); +// after +string name = nameof(some.Expression(a,b)); +``` + +--- + +On Oct 20, 2014 at 4:21 PM @Przemyslaw wrote: + +Regarding methods - I don't understand why we cannot have both worlds - easy and safe. First, I think we should allow to go without parameters if there is only single method. In case of multiple overloads, user should specify one explicitly. But it should be possible in simplest possible way: +``` +nameof(ToString(string, IFormatProvider)); +``` +and not +``` +nameof(ToString(default(string), default(IFormatProvider))); +``` +Simple type name should be enough. Why should we go with ```default(IFormatProvider)``` when simple ```IFormatProvider``` carries the same information? Why should we be forced to introduce variables when there are e.g. out or ref parameters involved in method call? + +Another thing: I cannot have instance and static members of the same name. Name is, well, name. It is meta. It should not matter if we are in "instance" or "static" context. Such information is irrelevant for the name of the member. It is weird that ```nameof(C.X)``` works with static X property of type C but does not work with instance property X of type C. Resulting name is the same after all and does not care about existence of static keyword. ```nameof(default(C).X)``` is too much ceremony for me. ```nameof(C.X)``` should work always. +As you may guess, I am not fan of ```nameof(this.p)``` for resolving field name. I think ```nameof(p)``` should be enough. Current class should be default when no other is provided. And if p clashes with name of local or parameter, or if one prefers to be explicit, then ```nameof(ClassName.x)``` should do the trick. + +It also does not feel right I can write ```typeof(int)``` but I cannot write ```nameof(int)```. + +And the last note. I don't like the visibility restriction at all. nameof is introduced to handle magic strings. One of important use case is reflection. And we resort to reflection when we cannot access things directly - to access internal and private things. It is very unfortunate we cannot use nameof in such case. + +Summing up - I don't see what are advantages of situation where "instance" world is married with meta world. But I can see many issues with such marriage. + +--- + +On Oct 20, 2014 at 10:51 PM @Ollka wrote: + +As a tool developer I should prefer OPTION 2: it is MUCH more easier to implement this case. + +But the invocation inside the nameof operator never calculates - it looks realy strange and misleading. So in my opinion OPTION 0 is not so bad. + +OPTION 1 is trying to avoid arguments in invocations. It make sense. But there is also one more large problem except arguments: the need to use 'default' expressions. Possible we can add to OPTION 1 the possibility to invoke instance method like static (from OPTION 0). And make it more strict in this case: the last expression should looks like 'expression.identifier' without '' or '(invocation arguments)'. Something like OPTION 0 but with possibility to use any expression as qualifier. + +The rename and other tool features should work without doubts if there is one candidate. And should operate with a set of candidates similar to the late bound case (when arguments are dynamic). When you rename one of the candidates the tool should warn about 'nameof' usage. + +--- + +On Oct 20, 2014 at 10:58 PM @MgSam wrote: + +I'm for keeping "Principle (2) says the nameof argument must resolve to one single symbol". Yes, it may be more cumbersome to write, but undoubtedly analyzers and other tools will be written to help completion for these invocations. It's better to be unambiguous. + +Of course, if it is possible to allow method groups for the special case where there is only one overload, that would certainly be a nice optimization. True, it means adding an overload could break the build, but this is pretty low cost as it's just at build time, not run time. Since `nameof` is purely a compile-time sugar you don't have the risk of a 3rd party shipping a library that could be accidentally broken by client code. + +--- + +On Oct 21, 2014 at 7:57 PM @lwischik wrote: + +Based on feedback, I've produced v4 of the "nameof" spec. I started a new discussion thread for it. Let's close this thread, and all move to the new one. +https://roslyn.codeplex.com/discussions/570364 + + + + +--- + diff --git a/meetings/2014/LDM-2014-10-23.md b/meetings/2014/LDM-2014-10-23.md new file mode 100644 index 0000000..4adaab1 --- /dev/null +++ b/meetings/2014/LDM-2014-10-23.md @@ -0,0 +1,1344 @@ +# nameof operator: spec v5 +The nameof(.) operator has the form nameof(expression). The expression must have a name, and may refer to either a single symbol or a method-group or a property-group. Depending on what the argument refers to, it can include static, instance and extension members. + +This is v5 of the spec for the "nameof" operator. [[v1](https://roslyn.codeplex.com/discussions/552376), [v2](https://roslyn.codeplex.com/discussions/552377), [v3](https://roslyn.codeplex.com/discussions/570115), [v4](https://roslyn.codeplex.com/discussions/570364)]. The key decisions and rationales are summarized below. _Please let us know what you think!_ + + +# Rationale + +Question: why do we keep going back-and-forth on this feature? + +Answer: I think we're converging on a design. It's how you do language design! (1) make a proposal, (2) _spec it out_ to flush out corner cases and make sure you've understood all implications, (3) _implement the spec_ to flush out more corner cases, (4) try it in practice, (5) if what you hear or learn at any stage raises concerns, goto 1. + +* _v1/2 had the problem "Why can't I write nameof(this.p)? and why is it so hard to write analyzers?"_ +* _v3 had the problem "Why can't I write nameof(MyClass1.p)? and why is it so hard to name method-groups?"_ +* _v4 had the problem "What name should be returned for types? and how exactly does it relate to member lookup?"_ + +This particular "nameof v5" proposal came from a combined VB + C# LDM on 2014.10.22. We went through the key decisions: + +1. __Allow to dot an instance member off a type? Yes.__ Settled on the answer "yes", based on the evidence that v1/v2 had it and it worked nicely, and v3 lacked it and ended up with unacceptably ugly "default(T)" constructions. +2. __Allow to dot instance members off an instance? Yes.__ Settled on the answer "yes", based on the evidence that v1/v2 lacked it and it didn't work well enough when we used the CTP, primarily for the case "this.p" +3. __Allow to name method-groups? Yes.__ Settled on the answer "yes", based on the evidence that v1/v2 had it and it worked nicely, and v3 lacked it and ended up with unacceptably ugly constructions to select which method overload. +4. __Allow to unambiguously select a single overload? No.__ Settled on the answer "no" based on the evidence that v3 let you do this but it looked too confusing. I know people want it, and it would be a stepping stone to infoof, but at LDM we rejected these (good) reasons as not worth the pain. The pain is that the expressions look like they'll be executed, and it's unclear whether you're getting the nameof the method or the nameof the result of the invocation, and they're cumbersome to write. +5. __Allow to use nameof(other-nonexpression-types)? No.__ Settled on the answer "only nameof(expression)". I know people want other non-expression arguments, and v1/v2 had them, and it would be more elegant to just write nameof(List<>.Length) rather than having to specify a concrete type argument. But at LDM we rejected these (good) reasons as not worth the pain. The pain is that the language rules for member access in expressions are too different from those for member access in the argument to nameof(.), and indeed member access for StrongBox<>.Value.Length doesn't really exist. The effort to unify the two concepts of member access would be way disproportionate to the value of nameof. This principle, of sticking to existing language concepts, also explains why v5 uses the standard language notions of "member lookup", and hence you can't do nameof(x.y) to refer to both a method and a type of name "y" at the same time. +6. __Use source names or metadata names? Source.__ Settled on source names, based on the evidence... v1/v2/v3 were source names because that's how we started; then we heard feedback that people were interested in metadata names and v4 explored metadata names. But I think the experiment was a failure: it ended up looking like _disallowing_ nameof on types was better than picking metadata names. At LDM, after heated debate, we settled on source names. For instance `using foo=X.Y.Z; nameof(foo)` will return "foo". Also `string f() => nameof(T)` will return "T". There are pros and cons to this decision. In its favor, it keeps the rules of nameof very simple and predictable. In the case of nameof(member), it would be a hindrance in most (not all) bread-and-butter cases to give a fully qualified member name. Also it's convention that "System.Type.Name" refers to an unqualified name. Therefore also nameof(type) should be unqualified. If ever you want a fully-qualified type name you can use typeof(). If you want to use nameof on a type but also get generic type parameters or arguments then you can construct them yourself, e.g. nameof(List) + "`2". Also the languages have no current notion of metadata name, and metadata name can change with obfuscation. +7. __Allow arbitrary expressions or just a subset?__ We want to try out the proposal "just a subset" because we're uneasy with full expressions. That's what v5 does. We haven't previously explored this avenue, and it deserves a try. +8. __Allow generic type arguments?__ Presumably 'yes' when naming a type since that's how expression binding already works. And presumably 'no' when naming a method-group since type arguments are used/inferred during overload resolution, and it would be confusing to also have to deal with that in nameof. [this item 8 was added after the initial v5 spec] + + +I should say, we're not looking for unanimous consensus -- neither amongst the language design team nor amongst the codeplex OSS community! We hear and respect that some people would like something closer to infoof, or would make different tradeoffs, or have different use-cases. On the language design team we're stewards of VB/C#: we have a responsibility to listen to and understand _every opinion_, and then use our own judgment to weigh up the tradeoffs and use-cases. I'm glad we get to do language design in the open like this. We've all been reading the comments on codeplex and elsewhere, our opinions have been swayed, we've learned about new scenarios and issues, and we'll end up with a better language design thanks to the transparency and openness. I'm actually posting these notes on codeplex as my staging ground for sharing them with the rest of the team, so the codeplex audience really does see everything "in the raw". + +# C# Syntax +``` +expression: ... | nameof-expression + +name-of-expression: + nameof ( expression ) +``` +In addition to the syntax indicated by the grammar, there are some additional syntactic constraints: (1) the argument expression can only be made up out of simple-name, member-access, base-access, or "this", and (2) cannot be simply "this" or "base" on its own. These constraints ensure that the argument looks like it has a name, and doesn't look like it will be evaluated or have side effects. I found it easier to write the constraints in prose than in the grammar. + +[clarification update] Note that member-access has three forms, `E.I` and `predefined-type.I` and `qualified-alias-member.I`. All three forms are allowed, although the first case `E` must only be made out of allowed forms of expression. + +If the argument to nameof at its top level has an unacceptable form of expression, then it gives the error "This expression does not have a name". If the argument contains an unacceptable form of expression deeper within itself, then it gives the error "This sub-expression cannot be used as an argument to nameof". + +It is helpful to list some things not allowed as the nameof argument: +``` + invocation-expression e(args) + assignment x += 15 + query-expression from y in z select y + lambda-expression () => e + conditional-expression a ? b : c + null-coalescing-expression a?? b + binary-expression ||, &&, |, ^, &, ==, !=, + <, >, <=, >=, is, as, <<, + >>, +, -, *, /, % + prefix-expression +, -, !, ~, ++, --, + *, &, (T)e + postfix-expression ++, -- + array-creation-expression new C[…] + object-creation-expression new C(…) + delegate-creation-expression new Action(…) + anonymous-object-creation-expression new {…} + typeof-expression typeof(int) + checked-expression checked(…) + unchecked-expression unchecked(…) + default-value-expression default(…) + anonymous-method-expression delegate {…} + pointer-member-access e->x + sizeof-expression sizeof(int) + literal "hello", 15 + parenthesized-expression (x) + element-access e[i] + base-access-indexed base[i] + await-expression await e + nameof-expression nameof(e) + vb-dictionary-lookup e!foo +``` + +Note that there are some types which are not counted as expressions by the C# grammar. These are not allowed as nameof arguments (since the nameof syntax only allows expressions for its argument). There's no need to spell out that the following things are not valid expressions, since that's already said by the language syntax, but here's a selection of some of the types that are not expressions: +``` + predefined-type int, bool, float, object, + dynamic, string + nullable-type Customer? + array-type Customer[,] + pointer-type Buffer*, void* + qualified-alias-member A::B + void void + unbound-type-name Dictionary<,> +``` + + +# Semantics + +The nameof expression is a constant. In all cases, nameof(...) is evaluated at compile-time to produce a string. Its argument is not evaluated at runtime, and is considered unreachable code (however it does not emit an "unreachable code" warning). + +_Definite assignment._ The same rules of definite assignment apply to nameof arguments as they do to all other unreachable expressions. + +_Name lookup_. In the following sections we will be discussing member lookup. This is discussed in $7.4 of the C# spec, and is left unspecified in VB. To understand nameof it is useful to know that the existing member lookup rules in both languages either return a single type, or a single instance/static field, or a single instance/static event, or a property-group consisting of overloaded instance/static properties of the same name (VB), or a method-group consisting of overloaded instance/static/extension methods of the same name. Or, lookup might fail either because no symbol was found or because ambiguous conflicting symbols were found that didn't fall within the above list of possibilities. + +_Argument binding_. The nameof argument refers to one or more symbols as follows. + +__nameof(simple-name)__, of the form I or I +The normal rules of simple name lookup $7.6.2 are used but with one difference... +* The third bullet talks about member lookup of I in T with K type arguments. Its third sub-bullet says _"Otherwise, the result is the same as a member access of the form T.I or T.I. In this case, it is a binding-time error for the simple-name to refer to an instance member."_ For the sake of nameof(simple-name), this case does not constitute a binding-time error. + + +__nameof(member-access)__, of the form E.I or E.I +The normal rules of expression binding are used to evaluate "E", with _no changes_. After E has been evaluated, then E.I is evaluated as per the normal rules of member access $7.6.4 but with some differences... +* The third bullet talks about member lookup of I in E. Its sub-bullets have rules for binding when I refers to static properties, fields and events. For the sake of nameof(member-access), each sub-bullet applies to instance properties, fields and events as well. +* The fourth bullet talks about member lookup of I in T. Its sub-bullets have rules for binding when I refers to instance properties, fields and events. For the sake of nameof(member-access), each sub-bullet applies to static properties, fields and events as well. + +__nameof(base-access-named)__, of the form base.I or base.I +This is treated as nameof(B.I) or nameof(B.I where B is the base class of the class or struct in which the construct occurs. + + +_Result of nameof_. The result of nameof is the identifier "I" with the _standard identifier transformations_. Note that, at the top level, every possible argument of nameof has "I". + +[update that was added after the initial v5 spec] If "I" binds to a method-group and the argument of nameof has generic type arguments at the top level, then it produces an error "Do not use generic type arguments to specify the name of methods". Likewise for property-groups. + +The standard identifier transformations in C# are detailed in $2.4.2 of the C# spec: first any leading @ is removed, then Unicode escape sequences are transformed, and then any formatting-characters are removed. This of course still happens at compile-time. In VB, any surrounding [] is removed + +# Implementation +In C#, nameof is stored in a normal InvocationExpressionSyntax node with a single argument. That is because in C# 'nameof' is a contextual keyword, which will only become the "nameof" operator if it doesn't already bind to a programmatic symbol named "nameof". TO BE DECIDED: what does its "Symbol" bind to? + +In VB, NameOf is a reserved keyword. It therefore has its own node: +```vb +Class NameOfExpressionSyntax : Inherits ExpressionSyntax + Public ReadOnly Property Argument As ExpressionSyntax +End Class +``` +TO BE DECIDED: Maybe VB should just be the same as C#. Or maybe C# should do the same as VB. + +What is the return value from `var r = semanticModel.GetSymbolInfo(argument)`? In all cases, r.Candidates is the list of symbol. If there is only one symbol then it is in r.Symbol; otherwise r.Symbol is null and the reason is "ambiguity". + +Analyzers and the IDE will just have to deal with this case. + + +# IDE behavior +```cs +class C { + [3 references] static void f(int i) {...nameof(f)...} + [3 references] void f(string s) {...nameof(this.f)...} + [3 references] void f(object o) {...nameof(C.f)...} +} +static class E { + [2 references] public static void f(this C c, double d) {} +} +``` + +__Highlight symbol from argument__: When you set your cursor on an argument to nameof, it highlights all symbols that the argument bound to. In the above examples, the simple name "nameof(f)" binds to the three members inside C. The two member access "nameof(this.f)" and "nameof(C.f)" both bind to extension members as well. + +__Highlight symbol from declaration__: When you set your cursor on any declaration of f, it highlights all nameof arguments that bind to that declaration. Setting your cursor on the extension declaration will highlight only "this.f" and "C.f". Setting your cursor on any member of C will highlight both all three nameof arguments. + +__Goto Definition__: When you right-click on an argument to nameof in the above code and do GoToDef, it pops up a FindAllReferences dialog to let you chose which declaration. (If the nameof argument bound to only one symbol then it would go straight to that without the FAR dialog.) + +__Rename declaration__: If you do a rename-refactor on one of the declarations of f in the above code, the rename will only rename this declaration (and will not rename any of the nameof arguments); the rename dialog will show informational text warning you about this. If you do a rename-refactor on the _last remaining_ declaration of f, then the rename will also rename nameof arguments. Note that if you turn on the "Rename All Overloads" checkbox of rename-refactor, then it will end up renaming all arguments. + +__Rename argument__: If you do a rename-refactor on one of the nameof arguments in the above code, the rename dialog will by default check the "Rename All Overloads" button. + +__Expand-reduce__: The IDE is free to rename "nameof(p)" to "nameof(this.p)" if it needs to do so to remove ambiguity during a rename. This might make nameof now bind to more things than it used to... + +__Codelens__: We've articulated the rules about what the argument of nameof binds to. The CodeLens reference counts above are a straightforward consequence of this. + + +## Bread and butter cases +```cs +// Validate parameters +void f(string s) { + if (s == null) throw new ArgumentNullException(nameof(s)); +} +``` + +```cs +// MVC Action links +<%= Html.ActionLink("Sign up", + @typeof(UserController), + @nameof(UserController.SignUp)) +%> +``` + +```cs +// INotifyPropertyChanged +int p { + get { return this._p; } + set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); } +} +// also allowed: just nameof(p) +``` + +```cs +// XAML dependency property +public static DependencyProperty AgeProperty = DependencyProperty.Register(nameof(Age), typeof(int), typeof(C)); +``` + +```cs +// Logging +void f(int i) { + Log(nameof(f), "method entry"); +} +``` + +```cs +// Attributes +[DebuggerDisplay("={" + nameof(getString) + "()}")] +class C { + string getString() { ... } +} +``` + +# Examples + +```cs +void f(int x) { + nameof(x) +} +// result "x": Parameter (simple name lookup) +``` + +```cs +int x=2; nameof(x) +// result "x": Local (simple name lookup) +``` + +```cs +const x=2; nameof(x) +// result "x": Constant (simple name lookup) +``` + +```cs +class C { + int x; + ... nameof(x) +} +// result "x": Member (simple name lookup) +``` + +```cs +class C { + void f() {} + nameof(f) +} +// result "f": Member (simple name lookup) +``` + +```cs +class C { + void f() {} + nameof(f()) +} +// result error "This expression does not have a name" +``` + +```cs +class C { + void f(){} + void f(int i){} + nameof(f) +} +// result "f": Method-group (simple name lookup) +``` + +```cs +Customer c; ... nameof(c.Age) +// result "Age": Property (member access) +``` + +```cs +Customer c; ... nameof(c._Age) +// result error "_Age is inaccessible due to its protection level: member access +``` + +```cs +nameof(Tuple.Create) +// result "Create": method-group (member access) +``` + +```cs +nameof(System.Tuple) +// result "Tuple": Type (member access). This binds to the non-generic Tuple class; not to all of the Tuple classes. +``` + +```cs +nameof(System.Exception) +// result "Exception": Type (member access) +``` + +```cs +nameof(List) +// result "List": Type (simple name lookup) +``` + +```cs +nameof(List<>) +// result error "type expected": Unbound types are not valid expressions +``` + +```cs +nameof(List.Length) +// result "Length": Member (Member access) +``` + +```cs +nameof(default(List)) +// result error "This expression doesn't have a name": Not one of the allowed forms of nameof +``` + +```cs +nameof(default(List).Length) +// result error "This expression cannot be used for nameof": default isn't one of the allowed forms +``` + +```cs +nameof(int) +// result error "Invalid expression term 'int'": Not an expression. Note that 'int' is a keyword, not a name. +``` + +```cs +nameof(System.Int32) +// result "Int32": Type (member access) +``` + +```cs +using foo=System.Int32; +nameof(foo) +// result "foo": Type (simple name lookup) +``` + +```cs +nameof(System.Globalization) +// result "Globalization": Namespace (member access) +``` + +```cs +nameof(x[2]) +nameof("hello") +nameof(1+2) +// error "This expression does not have a name": Not one of the allowed forms of nameof +``` + +```vb +NameOf(a!Foo) +' error "This expression does not have a name": VB-specific. Not one of the allowed forms of NameOf. +``` + +```vb +NameOf(dict("Foo")) +' error "This expression does not have a name": VB-specific. This is a default property access, which is not one of the allowed forms. +``` + +```vb +NameOf(dict.Item("Foo")) +' error "This expression does not have a name": VB-specific. This is an index of a property, which is not one of the allowed forms. +``` + +```vb +NameOf(arr(2)) +' error "This expression does not have a name": VB-specific. This is an array element index, which is not one of the allowed forms. +``` + +```vb +Dim x = Nothing +NameOf(x.ToString(2)) +' error "This expression does not have a name": VB-specific. This resolves to .ToString()(2), which is not one of the allowed forms. +``` + +```vb +Dim o = Nothing +NameOf(o.Equals) +' result "Equals". Method-group. Warning "Access of static member of instance; instance will not be evaluated": VB-specific. VB allows access to static members off instances, but emits a warning. +``` + +```cs +[Foo(nameof(C))] +class C {} +// result "C": Nameof works fine in attributes, using the normal name lookup rules. +``` + +```cs +[Foo(nameof(D))] +class C { class D {} } +// result "D": Members of a class are in scope for attributes on that class +``` + +```cs +[Foo(nameof(f))] +class C { void f() {} } +// result "f": Members of a class are in scope for attributes on that class +``` + +```cs +[Foo(nameof(T))] +class C {} +// result error "T is not defined": A class type parameter is not in scope in an attribute on that class +``` + +```cs +[Foo(nameof(T))] void f { } +// result error "T not defined": A method type parameter is not in scope in an attribute on that method +``` + +```cs +void f([Attr(nameof(x))] int x) {} +// result error "x is not defined": A parameter is not in scope in an attribute on that parameter, or any parameter in the method +``` + +```vb +Function f() + nameof(f) +End Function +' result "f": VB-specific. This is resolved as an expression which binds to the implicit function return variable +``` + +```vb +NameOf(New) +' result error "this expression does not have a name": VB-specific. Not one of the allowed forms of nameof. Note that New is not a name; it is a keyword used for construction. +``` + +```vb +Class C + Dim x As Integer + Dim s As String = NameOf(x) +End Class +' result "x": Field (simple name lookup) +``` + +```cs +class C { + int x; + string s = nameof(x); +} +// result "x". Field (simple name lookup) +``` + +```cs +class C { + static int x; + string s = nameof(x); +} +// result "x". Field (simple name lookup) +``` + +```cs +class C { + int x; + string s = nameof(C.x); +} +// result "x". Member (member access) +``` + +```cs +class C { + int x; + string s = nameof(default(C).x); +} +// result error "This expression isn't allowed in a nameof argument" - default. +``` + +```cs +struct S { + int x; + S() {var s = nameof(x); ...} +} +// result "x": Field access (simple name lookup). Nameof argument is considered unreachable, and so this doesn't violate definite assignment. +``` + +```cs +int x; ... nameof(x); x=1; +// result "x": Local access (simple name lookup). Nameof argument is unreachable, and so this doesn't violate definite assignment. +``` + +```cs +int x; nameof(f(ref x)); +// result error "this expression does not have a name". +``` + +```cs +var @int=5; nameof(@int) +// result "int": C#-specific. Local (simple name lookup). The leading @ is removed. +``` + +```cs +nameof(m\u200c\u0065) +// result "me": C#-specific. The Unicode escapes are first resolved, and the formatting character \u200c is removed. +``` + +```vb +Dim [Sub]=5 : NameOf([Sub]) +' result "Sub": VB-specific. Local (simple name lookup). The surrounding [.] is removed. +``` + +```cs +class C { + class D {} + class D {} + nameof(C.D) +} +// result "D" and binds to the non-generic form: member access only finds the type with the matching arity. +``` + +```cs +class C where T:Exception { + ... nameof(C) +} +// result error: the type 'string' doesn't satisfy the constraints +``` + + +--- +--- + +On Oct 24, 2014 at 6:50 AM @lwischik wrote: + +@Przemyslaw: _"Anyway, whatever you pick, you will never please all of us when it comes to handling types. Just go with metadata name."_ +I agree we'll never please everyone :) + +@Przemyslaw: _"Will this code work?"_ `using System.Console; class C { nameof(WriteLine) }`. +That's a good question. Yes it will work, because the "simple name lookup" rules have been modified to include static imports, and nameof re-uses those rules. + +@Halo_Four: _"As it stands nameof() is pointless for reflection and cannot be used with attributes that expect to be provided a type name with which the attribute will attempt to resolve a type."_ +Just to be clear, with context, nameof is a great _addition_ to typeof for reflection purposes, but not a good _substitute_ for it. Most of the bread-and-butter cases use nameof to achieve stuff with reflection that couldn't be done well before - DependencyProperty.Register (which uses typeof() to get the type and then nameof() to get a related property); PropertyChangedEventArgs (where the type is implicit and nameof() gets the property); [DebuggerDisplay] (where the type is implicit and nameof() gets the method); inside a proposed HtmlAction (again using typeof() to get the type and then nameof() to get the method). It's not a universal solution of course - doesn't work with method or property overloads - but it's got good points. + +@Halo_Four: _"I don't imagine that what I propose will get consideration by the design team but I do think it covers most of those bases"_. +Sure it will! You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants. What scenarios do you have in mind that need a constant, other than attributes which are already served by typeof? What would happen for the case of a generic type parameter where it can't be constant? What about the fact that compile-time constants would be based on reference assemblies which typically have different names from the runtime assemblies and so would have different AssemblyQualifiedNames? Do you see a clean way to alter the rules of member access to explain why these are constants? What would happen in ProjectN where assembly qualified names might not mean as much? Actually, most importantly, can you give code samples which would be cleaner under your proposal? (either as compared to VS2013, or compared to the nameof proposal?) + + + +--- + +On Oct 24, 2014 at 8:57 AM @Przemyslaw wrote: + +**lwischik wrote:** +> @Przemyslaw: _"Anyway, whatever you pick, you will never please all of us when it comes to handling types. Just go with metadata name."_ +> I agree we'll never please everyone :) +> +Source code name is as good as other choices. I am happy :) + +One note more. Note, I am not VB'er, so it might be stupid question. What if in VB one will use +``` +Public Shared AgeProperty As DependencyProperty = DependencyProperty.Register(NameOf(age), GetType(Integer), GetType(C)) +``` +Note usage of __age__ to denote name of __Age__ property. Will `nameof(age)` return "age" - taken from source in usage location - or "Age" - taken from source in declaration location? + +--- + +On Oct 24, 2014 at 9:23 AM @PauloMorgado wrote: + +**lwischik wrote:** +> 5. __Allow to use nameof(other-nonexpression-types)? No.__ Settled on the answer "only nameof(expression)". I know people want other non-expression arguments, and v1/v2 had them, and it would be more elegant to just write nameof(List<>.Length) rather than having to specify a concrete type argument. But at LDM we rejected these (good) reasons as not worth the pain. The pain is that the language rules for member access in expressions are too different from those for member access in the argument to nameof(.), and indeed member access for StrongBox<>.Value.Length doesn't really exist. The effort to unify the two concepts of member access would be way disproportionate to the value of nameof. This principle, of sticking to existing language concepts, also explains why v5 uses the standard language notions of "member lookup", and hence you can't do nameof(x.y) to refer to both a method and a type of name "y" at the same time. + +Totally agree with what's said about __nameof__. + +However, I'd like some improvements on what an expression is that would allow `List<>.Length` to be an expression and, therefor, `nameof(List<>.Length)` to be valid. + +**lwischik wrote:** +> 6. __Use source names or metadata names? Source.__ Settled on source names, based on the evidence... v1/v2/v3 were source names because that's how we started; then we heard feedback that people were interested in metadata names and v4 explored metadata names. But I think the experiment was a failure: it ended up looking like _disallowing_ nameof on types was better than picking metadata names. At LDM, after heated debate, we settled on source names. For instance `using foo=X.Y.Z; nameof(foo)` will return "foo". Also `string f() => nameof(T)` will return "T". There are pros and cons to this decision. In its favor, it keeps the rules of nameof very simple and predictable. In the case of nameof(member), it would be a hindrance in most (not all) bread-and-butter cases to give a fully qualified member name. Also it's convention that "System.Type.Name" refers to an unqualified name. Therefore also nameof(type) should be unqualified. If ever you want a fully-qualified type name you can use typeof(). If you want to use nameof on a type but also get generic type parameters or arguments then you can construct them yourself, e.g. nameof(List) + "`2". Also the languages have no current notion of metadata name, and metadata name can change with obfuscation. + +Isn't obfuscation a post compile-time task? Are you saying it can be a compile-time task? + +Regarding `string f() => nameof(T)` returning __T__, it makes sense as it's the only way to get a constant out of the expression, but i don't like that `using foo=X.Y.Z; nameof(foo)` returns __foo__. `foo` is an alias and `T` isn't. + + + + + +--- + +On Oct 24, 2014 at 1:18 PM @KathleenDollard wrote: + +Looks good. I think the potential pain points can be discussed in a future rev if they are real pain points. For some of this, I wonder if we are actually looking at features better solved with compiler extensions – the ability to define things resolved at compile time. + +First responses – not deeply thought + +- On NameOfExpressionSyntax in VB – (I am working with the API’s) don’t care. There are enough differences between the two that + +Rename declaration: If you do a rename-refactor on one of the declarations of f in the above code, the rename will only rename this declaration (and will not rename any of the nameof arguments); the rename dialog will show informational text warning you about this. If you do a rename-refactor on the last remaining declaration of f, then the rename will also rename nameof arguments. Note that if you turn on the "Rename All Overloads" checkbox of rename-refactor, then it will end up renaming all arguments. + +Rename argument: If you do a rename-refactor on one of the nameof arguments in the above code, the rename dialog will by default check the "Rename All Overloads" button. + +- Do you have a mockup of the “rename dialog”. I hit Ctl-Period Enter without a dialog when I do rename refactor. + +From the conversation + +You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants. + +- Cool! Is that happening? + + +--- + +On Oct 24, 2014 at 1:21 PM @agat50 wrote: + +I think allowing method groups and disallowing nameof(Func()) is mistake. As it was discussed earlier, what about refactoring? Why not allow both versions? And nameof(F1), and nameof(f1(default(int)) or nameof(f1(int)) whatsoever. Due to such behavior i'd rather use old slow expression handmade nameof for methods unfortunately. + +--- + +On Oct 24, 2014 at 2:53 PM @lwischik wrote: + +**Przemyslaw wrote:** +> What if in VB you declare a property `Age` but then do `NameOf(age)` with different capitalization - will it pick argument or declaration capitalization? + +Good question. There are other related cases. What if there was declared two overloads `Sub fred()` and `Sub Fred()` and you do `NameOf(Me.FRED)` which will it pick? Answer in all cases is that the argument to NameOf has the form "I" or "E.I" and the result is simply "I" -- i.e. use the capitalization of the argument to NameOf, not that of the declaration. + + +@PauloMorgado: the (small) point about obfuscation is that if there's a post-compile-time step that renames types, and you tried to make nameof return metadata names, then you might end up with nameof(Customer) returning "Customer`1" but typeof(Customer).Name returning "_CS`1" at runtime: it means that the compiler's attempt to anticipate the final metadata name was flawed. + + +@Kathleen: hit Ctrl+R Ctrl+R to get the inline rename experience. (At least that's the shortcut in General profile). I think there's an F key for it as well but I don't know. Good point that we'll also have to consider Ctrl+Dot. + +--- + +On Oct 24, 2014 at 3:36 PM @Halo_Four wrote: + +**lwischik wrote:** +> @Halo_Four: _"As it stands nameof() is pointless for reflection and cannot be used with attributes that expect to be provided a type name with which the attribute will attempt to resolve a type."_ +> Just to be clear, with context, nameof is a great _addition_ to typeof for reflection purposes, but not a good _substitute_ for it. Most of the bread-and-butter cases use nameof to achieve stuff with reflection that couldn't be done well before - DependencyProperty.Register (which uses typeof() to get the type and then nameof() to get a related property); PropertyChangedEventArgs (where the type is implicit and nameof() gets the property); [DebuggerDisplay] (where the type is implicit and nameof() gets the method); inside a proposed HtmlAction (again using typeof() to get the type and then nameof() to get the method). It's not a universal solution of course - doesn't work with method or property overloads - but it's got good points. + +I spoke a little too absolutist in my original statement. I agree that `nameof()` does have various uses and can aid in reflection scenarios especially when resolving member names, and I think that it's great for resolving the names of local parameters. + +These changes are nice but I don't think that they really address the core problem which is the potential confusion over how it is supposed to be used with type names. Returning "List" for `List` isn't that much of an improvement since "List" is still not terribly useful. + +And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time. + +> @Halo_Four: _"I don't imagine that what I propose will get consideration by the design team but I do think it covers most of those bases"_. +> Sure it will! You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants. What scenarios do you have in mind that need a constant, other than attributes which are already served by typeof? + +Thanks for giving me the opportunity to expand upon the idea. + +These expressions would result in constants only when applied to scenarios expecting a constant, such as assignment to a constant or passing to an attribute. I understand that having two behaviors could be considered confusing, then again `typeof()` already has two behaviors for exactly this purpose although the implementation is pretty well hidden. + +> What would happen for the case of a generic type parameter where it can't be constant? + +I'd probably go with a compile-time error. If the programmer intended to include an open generic type they could explicitly specify `typeof(List<>)` which is already legal today. + +> What about the fact that compile-time constants would be based on reference assemblies which typically have different names from the runtime assemblies and so would have different AssemblyQualifiedNames? + +When using `typeof()` with an attribute a constant string containing the fully qualified assembly name, including version and public token, is what is embedded in the attribute value blob. Being fully qualified is optional if the type is in the current assembly or in mscorlib although C# always emits the fully qualified type unless the type is in the current assembly, which is understandable as the compiler likely can't resolve that information at the time it would have to be emitted. As such using `typeof(Foo).AssemblyQualifiedName` on a local type in such a situation should probably be a compile-time error. + +> Do you see a clean way to alter the rules of member access to explain why these are constants? + +That would probably require more text than would be reasonable to include in this forum. :) I'd probably have to look at how the spec currently handles the duality of `typeof()`, although granted that doesn't resolve as a member access. I can understand that this would be a sticking point because I am redefining a construct of the language. + +> What would happen in ProjectN where assembly qualified names might not mean as much? Actually, most importantly, can you give code samples which would be cleaner under your proposal? (either as compared to VS2013, or compared to the nameof proposal?) + +It's more a question of intent. The problem I have with `nameof()` with types is that there are multiple scenarios in which a type name could be useful and the value of that type name would be different for those scenarios. Having `typeof()` support this kind of syntax (or through another syntax) gives the programmer the opportunity to be explicit. The prototypical code example would probably be as follows: + +```cs +[AttributeUsage(AttributeTargets.Class)] +public class SomeAttribute : Attribute { + public string TypeName { get; set; } +} + +[SomeAttribute(typeof(Bar).AssemblyQualifiedName)] +public class Foo { + private const string FallbackType = typeof(Baz).AssemblyQualifiedName; + + public Foo() { + object[] attributes = typeof(Foo).GetCustomAttributes(typeof(SomeAttribute), false); + SomeAttribute attribute = (SomeAttribute)attributes[0]; + Type type = Type.GetType(attribute.TypeName); + if (type == null) { + type = Type.GetType(FallbackType); + } + object instance = Activator.CreateInstance(type); + } +} +``` + +--- + +On Oct 24, 2014 at 4:36 PM @madrian wrote: + +**lwischik wrote:** +> **Przemyslaw wrote:** +> > What if in VB you declare a property `Age` but then do `NameOf(age)` with different capitalization - will it pick argument or declaration capitalization? +> +> Good question. There are other related cases. What if there was declared two overloads `Sub fred()` and `Sub Fred()` and you do `NameOf(Me.FRED)` which will it pick? Answer in all cases is that the argument to NameOf has the form "I" or "E.I" and the result is simply "I" -- i.e. use the capitalization of the argument to NameOf, not that of the declaration. + +Might be a good idea to have an IDE warning about casing. WPF bindings will not update if the argument to nameof is cased incorrectly in property change. + + + + +--- + +On Oct 24, 2014 at 4:49 PM @supercat wrote: + +**lwischik wrote:** +> __Rename declaration__: If you do a rename-refactor on one of the declarations of f in the above code, the rename will only rename this declaration (and will not rename any of the nameof arguments); the rename dialog will show informational text warning you about this. If you do a rename-refactor on the _last remaining_ declaration of f, then the rename will also rename nameof arguments. Note that if you turn on the "Rename All Overloads" checkbox of rename-refactor, then it will end up renaming all arguments. + +Is the above behavior affected by the location of overloads in either the present class or superclasses? For example, if a class contains an overload for `foo(int)` and no others, but the superclass contains others overloads, would `nameof(this.foo)` be unaffected? What if the only overload in the base class was `foo(int)`, which would become accessible when the child-type definition was renamed? + +I would guess that logical behavior would be to base the decision of whether to rename the `nameof()` operands depend upon whether there was more than one definition of the name *visible in the present scope before the rename*, not whether there would be any definitions with that name visible in the present scope afterward, but it might be good to clarify that. + +--- + +On Oct 24, 2014 at 5:51 PM @MgSam wrote: + +**Halo_Four wrote:** +> And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time. + +Allowing method groups now doesn't preclude a syntax for choosing overloads at some future date. However, I think it's important to keep in mind during this discussion that after `nameof` is added, the benefit for then adding `infoof` later becomes even less worth the cost. + +I'm fine with the new spec. The only thing I really don't like is that `nameof` on an alias gives you the name of the alias rather than the target of the alias. Even though implementation and spec might be simpler by doing this, it's still a misfeature- when would anyone ever want this behavior? It's bound to lead to unintentional bugs. + +--- + +On Oct 24, 2014 at 8:54 PM @Halo_Four wrote: + +**MgSam wrote:** +> **Halo_Four wrote:** +> > And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time. +> +> Allowing method groups now doesn't preclude a syntax for choosing overloads at some future date. However, I think it's important to keep in mind during this discussion that after `nameof` is added, the benefit for then adding `infoof` later becomes even less worth the cost. + +I agree, but even if a new syntax is added to support overload resolution we'll be permanently left with the ambiguous version. Best we could hope for on revision is a warning. + +What are the use cases for taking the name of a method group like that, anyway? Any reflection scenario I can think of would probably fail at run time due to the ambiguity of the name. Whatever consumed the name would have to manually resolve the desired override by method signature. + +> I'm fine with the new spec. The only thing I really don't like is that `nameof` on an alias gives you the name of the alias rather than the target of the alias. Even though implementation and spec might be simpler by doing this, it's still a misfeature- when would anyone ever want this behavior? It's bound to lead to unintentional bugs. + +Hm, in the [v4](https://roslyn.codeplex.com/discussions/570364) spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo. + +--- + +On Oct 24, 2014 at 9:39 PM @Skiminok wrote: + +**Halo_Four wrote:** +> Hm, in the [v4](https://roslyn.codeplex.com/discussions/570364) spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo. + +That's not a typo in this case, I think, In v4, __nameof__ consistently returned metadata names, whereas in v5, it consistently returns source names. Since "foo" is, in fact, a source name for Int32 in this case, that is what __nameof__ is going to return. + +I don't like it at all, frankly speaking. There's a difference between "T" in `string f() => nameof(T)` and "foo" in `using foo=X.Y.Z; nameof(foo)`. The first one returns a _source name_ for an object that exists both at compile-time and at run-time — a generic type argument. Even though at run-time this argument is replaced with a specific type, the notion of a generic type argument still exists in metadata. +In contrast, type aliases are purely a _source-only feature_. Reflection doesn't know anything about type aliases. I cannot imagine any possible usage for returning a constant string that denotes a compile-time only object... but at run-time. + +I personally have always thought about type aliases in terms of α-conversion. A type alias "foo" is simply a local renaming for a type "Int32". It is an alias, an indirect reference, which disappears at run-time. Giving an evidence of its existence at run-time with __nameof__ breaks that neat encapsulation philosophy. + +--- + +On Oct 25, 2014 at 3:11 AM @Halo_Four wrote: + +**Skiminok wrote:** +> **Halo_Four wrote:** +> > Hm, in the [v4](https://roslyn.codeplex.com/discussions/570364) spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo. +> +> That's not a typo in this case, I think, In v4, __nameof__ consistently returned metadata names, whereas in v5, it consistently returns source names. Since "foo" is, in fact, a source name for Int32 in this case, that is what __nameof__ is going to return. + +You're right, it's point 6 of the v5 spec. + +> I don't like it at all, frankly speaking. There's a difference between "T" in `string f() => nameof(T)` and "foo" in `using foo=X.Y.Z; nameof(foo)`. The first one returns a _source name_ for an object that exists both at compile-time and at run-time — a generic type argument. Even though at run-time this argument is replaced with a specific type, the notion of a generic type argument still exists in metadata. +> In contrast, type aliases are purely a _source-only feature_. Reflection doesn't know anything about type aliases. I cannot imagine any possible usage for returning a constant string that denotes a compile-time only object... but at run-time. +> +> I personally have always thought about type aliases in terms of α-conversion. A type alias "foo" is simply a local renaming for a type "Int32". It is an alias, an indirect reference, which disappears at run-time. Giving an evidence of its existence at run-time with __nameof__ breaks that neat encapsulation philosophy. + +And therein lies the crux of the problem. You can pick metadata names or you can pick source names and either way you can guarantee that you'll still be wrong half of the time. I'll argue that I think that they should be metadata names, but complete metadata names and not partial names. + +I'll ask another unpopular question: do we really need to get the names for _all_ of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft. + +Oh, and I keep seeing logging bandied about as a use case for `nameof()`. That's ludicrous. Why should the programmer ever have to have the caller explicitly specify that it wants to be logged? Isn't that why caller info attributes exist? And I still think that they need to be expanded upon because the current batch is horribly limited. + + +--- + +On Oct 25, 2014 at 4:00 AM @agat50 wrote: + +**Halo_Four wrote:** + +> I'll ask another unpopular question: do we really need to get the names for _all_ of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft. + +I totally agree, nameof use-cases are mostly notifypropertychanged, log, raise exception\etc, no need to do universal nameof due to release is coming soon=). + +And i want say something about infoof() future operator. Do we actually need it? In c++ compile time types are crucial feature, "vector::const_iterator" etc are used everywhere, but c# has absolutely another design. For example, as i see possible infoof usage: + +```c# +public class A +{ + public void Func1(int a, double b) + {} + private infoof(A.Func1(int,double)).FirstParameter.ParameterType _f1 + = default(infoof(A.Func1(int,double)).FirstParameter.ParameterType); + public infoof(A.Func1(int,double)).ReturnType Func2( + string s, + infoof(A.Func1(int,double)).SecondParameter.ParameterType b + ) + { + Console.WriteLine( + "{0},{1},{2}", + s, + b, + infoof(A.Func1(int,double)).SecondParameter.ParameterName + ); + } +} +``` + +I don't see ANY use-case for such complex feature which costs effort. So probably we should just stick to common use-cases nameof() syntax without making it Big feature, which is useless, imho. + +--- + +On Oct 25, 2014 at 4:42 PM @nmgafter wrote: + +**Halo_Four wrote:** +> I'll ask another unpopular question: do we really need to get the names for _all_ of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft. + +[Failure of imagination - that is, failure to make language constructs orthogonal because you aren't quite sure how people will need to use them together - is one of the most common and severe errors you can make in programming language design. It is an error that is often impossible to correct after the fact. There is an approach to programming language design that you can take to maximize your opportunities to err in this way: consider the interactions of individual language features and decide on a case-by-case basis if the interaction will be allowed or not (or even what the semantics should be) based on whether or not you can find use cases and which semantics best fit those use cases. +](http://gafter.blogspot.com/2006/09/failure-of-imagination-in-language_17.html) + + +--- + +On Oct 25, 2014 at 6:49 PM @Halo_Four wrote: + +**nmgafter wrote:** +> **Halo_Four wrote:** +> > I'll ask another unpopular question: do we really need to get the names for _all_ of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft. +> +> [Failure of imagination - that is, failure to make language constructs orthogonal because you aren't quite sure how people will need to use them together - is one of the most common and severe errors you can make in programming language design. It is an error that is often impossible to correct after the fact. There is an approach to programming language design that you can take to maximize your opportunities to err in this way: consider the interactions of individual language features and decide on a case-by-case basis if the interaction will be allowed or not (or even what the semantics should be) based on whether or not you can find use cases and which semantics best fit those use cases. +> ](http://gafter.blogspot.com/2006/09/failure-of-imagination-in-language_17.html) + +I can understand that. However, `nameof()` is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax. + +My concern is the opposite of your statement. Once the behavior of the language feature is stamped and released we're all stuck with it permanently. Why dig pits before you can anticipate if they'll be success or failure? + +Don't get me wrong, I think the majority of scenarios with `nameof()` make perfect sense and I think that it is a worthwhile feature. In some other cases I agree that the scenario is conceptually useful but that there isn't a "right answer" as to what the return value should be and that `nameof()` by itself doesn't allow the programmer to declare enough of their intent. Even with namespaces, how do you know that the programmer doesn't want or need the full namespace? Does the programmer need to break out and concat multiple uses of `nameof()`? Are we walking into a `fullnameof()` in C# 7.0? + +--- + +On Oct 27, 2014 at 10:21 PM @supercat wrote: + +**Halo_Four wrote:** +Does the programmer need to break out and concat multiple uses of `nameof()`? Are we walking into a `fullnameof()` in C# 7.0? + +What about having an optional second argument to `nameof()` which would specify what is desired? That would seem better than trying to come up with one meaning to please everyone. + +--- + +On Oct 28, 2014 at 12:06 AM @nmgafter wrote: + +**Halo_Four wrote:** +> I can understand that. However, `nameof()` is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax. + +nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to _no scenarios_. + +Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario". + +As described above, we avoid designing language features scenario by scenario. + +--- + +On Oct 28, 2014 at 12:52 AM @Halo_Four wrote: + +**nmgafter wrote:** +> **Halo_Four wrote:** +> > I can understand that. However, `nameof()` is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax. +> +> nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to _no scenarios_. +> +> Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario". +> +> As described above, we avoid designing language features scenario by scenario. + +Which is absolutely great. But how do you design language features that have no "correct" answers without looking at the scenarios? How do you know how people _expect_ that feature to behave? If you end up with a ton of people on Stackoverflow asking why `nameof()` explodes at runtime when used with attributes is that considered a success? I don't think that any feature can be designed in a bottle. + +Because `nameof()` is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently. If that wasn't the case you wouldn't have the two-dozen or so different ways to figure out the result and we wouldn't be on v5 where the real bread-and-butter use-cases haven't changed at all. + +__Update:__ Rereading my comment I realize that I am probably coming off as belligerent or even angry. I want everyone to know that is the furthest from the case. I really enjoy these conversations and find the back and forth really enlightening both about the design process and as to the different approaches one can take to tackle these problems. Given any proposal I would probably seek to poke holes in it if only as a devil's advocate. I have a great deal of passion about this language and am very grateful for the effort that has gone into making it what it is and will continue to make it great in the future. I applaud the decision that made this process a bit more public than it has been in the past and apologize for the grief that these forums impose of those who work on the front lines. :) + +--- + +On Oct 28, 2014 at 12:54 AM @Halo_Four wrote: + +**supercat wrote:** +> **Halo_Four wrote:** +> Does the programmer need to break out and concat multiple uses of `nameof()`? Are we walking into a `fullnameof()` in C# 7.0? +> +> What about having an optional second argument to `nameof()` which would specify what is desired? That would seem better than trying to come up with one meaning to please everyone. + +Either way, my comment was one of capability, not implementation. Whether it might be `fullnameof(type)` or `nameof(type, full)` or some constant-evaluating flavor of `typeof(type).FullName` it's all about whether or not the developer can declare their intent. + +--- + +On Oct 28, 2014 at 2:05 AM @PauloMorgado wrote: + +**Halo_Four wrote:** +> **nmgafter wrote:** +> > **Halo_Four wrote:** +> > > I can understand that. However, `nameof()` is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax. +> > +> > nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to _no scenarios_. +> > +> > Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario". +> > +> > As described above, we avoid designing language features scenario by scenario. +> +> Which is absolutely great. But how do you design language features that have no "correct" answers without looking at the scenarios? How do you know how people _expect_ that feature to behave? If you end up with a ton of people on Stackoverflow asking why `nameof()` explodes at runtime when used with attributes is that considered a success? I don't think that any feature can be designed in a bottle. +> +> Because `nameof()` is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently. If that wasn't the case you wouldn't have the two-dozen or so different ways to figure out the result and we wouldn't be on v5 where the real bread-and-butter use-cases haven't changed at all. +> +> __Update:__ Rereading my comment I realize that I am probably coming off as belligerent or even angry. I want everyone to know that is the furthest from the case. I really enjoy these conversations and find the back and forth really enlightening both about the design process and as to the different approaches one can take to tackle these problems. Given any proposal I would probably seek to poke holes in it if only as a devil's advocate. I have a great deal of passion about this language and am very grateful for the effort that has gone into making it what it is and will continue to make it great in the future. I applaud the decision that made this process a bit more public than it has been in the past and apologize for the grief that these forums impose of those who work on the front lines. :) + +I think this feature is being thought independently of the scenario. It's people (me included) that is complaining about the scenarios it doesn't fit. + +--- + +On Oct 28, 2014 at 5:58 AM @nmgafter wrote: + +**Halo_Four wrote:** +> Because `nameof()` is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently. + +We use the use cases to validate that a design works as expected, and to help us decide when to select another design if most do not. We don't use it to guide the design for each use case independently. + +--- + +On Oct 28, 2014 at 7:14 AM @bradphelan wrote: + +I'm trying to understand the power of the nameof operator. The one place in my code where it seems to fit is where I am constantly using selector expressions. Especially in [ReactiveUI](https://github.com/reactiveui/ReactiveUI) we do stuff like + +``` +this + .WhenAnyValue(p => p.User.Name.FirstName ) + .Select( firstName => firstName.ToUpper) + .Subscribe(s => Console.WriteLine(s)); +``` +WhenAnyValue takes the selector expression and parses it and chains INPC handlers down the object heirarchy finally presenting the result as an _IObservable_. +The parsing of the selector expression however happens at runtime and involves some overhead. It most cases it does not affect application performance at all. Still I am wondering, could I use nameof to do something like + +``` +this + .WhenAnyValue(nameof(this.User.Name.FirstName) ) + .Select( firstName => firstName.ToUpper) + .Subscribe(s => Console.WriteLine(s)); +``` + +However in the _nameof_ examples above I don't see _nameof_ being used with property access more than one level deep. Is this a deliberate design choice? + +--- + +On Oct 28, 2014 at 1:09 PM @Ollka wrote: + +This version looks great. Thank you! + +Just to be sure: + +CASE 1: + +``` +void Foo(){} +void Foo(){} +void Test() +{ + var t = nameof(Foo); // points to Foo + var t2 = nameof(Foo); // is an error +} +``` + +CASE 2: + +``` +using C; +using C2; + +static class C +{ + public static void Foo(){} +} + +static class C2 +{ + public static int Foo {get;set;} +} + +public class C3 +{ + public void Test() + { + var t = nameof(Foo); // is an error + } +} +``` + +CASE 3: +``` +public class A +{ + protected void Foo(){} + protected static void Foo2(){} +} + +public class B: A +{ + public class C + { + public void Bar() + { + var t = nameof(A.Foo2); // points to Foo2 + var t2 = nameof(A.Foo); // is an error + } + } +} +``` + +Are all examples correct? + +--- + +On Oct 28, 2014 at 1:53 PM @Halo_Four wrote: + +**bradphelan wrote:** +> I'm trying to understand the power of the nameof operator. The one place in my code where it seems to fit is where I am constantly using selector expressions. Especially in [ReactiveUI](https://github.com/reactiveui/ReactiveUI) we do stuff like +> +> ``` +> this +> .WhenAnyValue(p => p.User.Name.FirstName ) +> .Select( firstName => firstName.ToUpper) +> .Subscribe(s => Console.WriteLine(s)); +> ``` +> WhenAnyValue takes the selector expression and parses it and chains INPC handlers down the object heirarchy finally presenting the result as an _IObservable_. +> The parsing of the selector expression however happens at runtime and involves some overhead. It most cases it does not affect application performance at all. Still I am wondering, could I use nameof to do something like +> +> ``` +> this +> .WhenAnyValue(nameof(this.User.Name.FirstName) ) +> .Select( firstName => firstName.ToUpper) +> .Subscribe(s => Console.WriteLine(s)); +> ``` +> +> However in the _nameof_ examples above I don't see _nameof_ being used with property access more than one level deep. Is this a deliberate design choice? + +I'd assume that even if it does parse that the result it would give you would be simply "FirstName". Since it also generates a string literal and doesn't do anything with the type of the property you'd also probably have to provide an overload of WhenAnyValue that required specification of the generic type, e.g. + +```cs +string name = nameof(this.User.Name.FirstName); +this + .WhenAnyValue(name) + .Select(firstName => firstName.ToUpper()) + .Subscribe(s => Console.WriteLine(s)); +``` + +--- + +On Oct 28, 2014 at 11:11 PM @supercat wrote: + +**Ollka wrote:** +> var t = nameof(Foo); // points to Foo + +Why would it not be `Foo`? Note that situation is very different from: + + string showEnumerableTypeName(T it) + { return nameof(IEnumerable); } + +In that case, I would expect `showEnumerableTypeName(5)` to return `IEnumerable` , since the code where `name()` is being evaluated knows the type only as `T` even though the caller knows it as a `IEnumerable`. + +> var t2 = nameof(A.Foo); // is an error + +I would think it should return the name of the instance method in class `A` if no static method exists. I don't see that requiring code to specify `nameof(default(A).Foo)` would be particularly helpful. + +--- + +On Oct 29, 2014 at 12:46 AM @Halo_Four wrote: + +**Ollka wrote:** +> CASE 1: +> +> ``` +> void Foo(){} +> void Foo(){} +> void Test() +> { +> var t = nameof(Foo); // points to Foo +> var t2 = nameof(Foo); // is an error +> } +> ``` + +`t` would point to `Foo` but the result would be "Foo" +`t2` would be a compiler error since there is no type `Foo` that has no generic type parameters + +> CASE 2: +> +> ``` +> using C; +> using C2; +> +> static class C +> { +> public static void Foo(){} +> } +> +> static class C2 +> { +> public static int Foo {get;set;} +> } +> +> public class C3 +> { +> public void Test() +> { +> var t = nameof(Foo); // is an error +> } +> } +> ``` + +I think that would be an error, neither Foo would be in scope. If `C3` inherited from either `C2` or `C1` then it would probably succeed and return "Foo". + +> CASE 3: +> ``` +> public class A +> { +> protected void Foo(){} +> protected static void Foo2(){} +> } +> +> public class B: A +> { +> public class C +> { +> public void Bar() +> { +> var t = nameof(A.Foo2); // points to Foo2 +> var t2 = nameof(A.Foo); // is an error +> } +> } +> } +> ``` + +`t` would be "Foo" and I do think that `t2` would be assigned "Foo" as point #1 of this proposal allows an instance member to be referenced from a type name. + +--- + +On Oct 29, 2014 at 7:51 PM @lwischik wrote: + +**Ollka wrote:** +> This version looks great. Thank you! +> Just to be sure: [snip] + +Thanks Ollka. You and one of our devs simultaneously and independently highlighted the need to think about generic arguments `nameof(I)` and `nameof(E.I)` when I binds to a method-group. I'll update the spec to indicate, _If I binds to a method-group and there are one or more generic arguments, then error "Don't provide generic arguments when taking method names"_. + +Separately, another of our devs pointed out the case `nameof(a::b)` vs `nameof(a::b.c)`. It looks like the spec already deals with that correctly: the first isn't allowed, but the second is. + +(I'll also follow up with an analysis of your cases, and add them to the "examples" section). + + +The way I always figure out examples is to try writing an invocation statement or member access or whatever :) That way I can tell whether the "simple name / member access" forms of expression can bind to a thing. Nameof can bind to a thing if and only if normal expressions can bind to it. After that I think carefully through special cases about instance/static. + +--- + +On Oct 29, 2014 at 9:32 PM @PauloMorgado wrote: + +**lwischik wrote:** +> Separately, another of our devs pointed out the case `nameof(a::b)` vs `nameof(a::b.c)`. It looks like the spec already deals with that correctly: the first isn't allowed, but the second is. +> +Do you mean that this is allowed: + +```C# +nameof(System) +``` + + +but this isn't: + +```C# +nameof(global::System) +``` + + +Is that it? + +--- + +On Oct 31, 2014 at 1:55 AM @lwischik wrote: + +**PauloMorgado wrote:** +> Do you mean that this is allowed `nameof(System)` but this isn't `nameof(global::System)` + +Yes, exactly. + +We discussed the overall nameof spec at LDM on Wednesday 2014.10.29. Notes: + +* For VB, MyClass.p is also allowed +* For VB, it should continue to allow dotting static member of an instance, and should continue to give a warning +* For C#, it should continue to disallow dotting static member of an instance +* For VB, the accessibility of return-types/parameters is treated differently (at a separate step) from accessibility of members. Must look into this. +* For VB, when you write "x.f" it can be a call to x.f with implicit parentheses. Must look into this to see if it affects anything. +* We're fine with disallowing nameof(global::System) for now. It would theoretically be nice, but its practical niceness doesn't seem to outweigh the additional spec complexity of adding it. At least up until there are howls of complaint with convincing examples where it's needed. +* We're fine with the subset of expressions identified above. +* For C#, the treatment of extension methods in this spec is incorrect. Name lookup only returns extension methods in VB not C#. This will require more work. + +For the final point, I reckon it should work on this principle: + +_"If there exist some set of arguments such that I can invoke x.f(args), then nameof(x.f) should bind to all symbols f that can be reached by various such arguments"._ (that explains why shadowing is respected in C#, and why nameof works with extension methods off instances) + +_"And whatever symbols nameof(x.f) can bind to, then nameof(C.f) should bind to exactly the same set, where C is the type of x"_. (that is the cross-cutting issue that Type.instance should work, and it stops nameof from breaking when instance methods get turned into extension methods). + +The trouble is that C# spec doesn't currently have any notion of name lookup for extension methods. That is only accounted-for as part of the extension method resolution spec. So it'll require legwork... + + + +--- + +On Nov 7, 2014 at 5:51 PM @Ollka wrote: + +**lwischik wrote:** +> * For C#, it should continue to disallow dotting static member of an instance + +Is it error if method-group contains both static and instance methods and qualifier expression is an instance? + +--- + +On Nov 11, 2014 at 12:11 AM @dougbu wrote: + +@lwischik +Looks like the spec doesn't currently describe conflicts between the `nameof` and an in-scope method with that name. That is + +> which will only become the "nameof" operator if it doesn't already bind to a programmatic symbol named "nameof". + +should be part of the specification and not the implementation notes. + +Separately the value of "`using foo=X.Y.Z; nameof(foo)` will return "foo". " isn't clear. I see a few questions on this and other perhaps-unnecessary features above e.g. those from @Halo_Four. But I haven't seen your response. What are your thoughts? My apologies if I've missing something above. + +--- + +On Nov 11, 2014 at 1:04 AM @lwischik wrote: + +**Ollka wrote:** +> [For C#, it should continue to disallow dotting static member of an instance] +> Is it error if method-group contains both static and instance methods and qualifier expression is an instance? + +That's a good question. I'll have to think about where that sits in the spec. What do you reckon? + +We also have to drastically re-spec the C# behavior of nameof for extension methods. That's because the current name lookup rules for extension methods are a bit goofy (they just say "do namelookup for x.f as if for an invocation expression" but that's not precise enough!) I'm hoping to get this spec'd up by later this week. + +**dougbu wrote:** +> Looks like the spec doesn't currently describe conflicts between the `nameof` and an in-scope method with that name. Should be part of the specification and not the implementation notes. +> Separately the value of "`using foo=X.Y.Z; nameof(foo)` will return "foo". " isn't clear. I see a few questions on this and other perhaps-unnecessary features above e.g. those from @Halo_Four. But I haven't seen your response. What are your thoughts? My apologies if I've missing something above. + +Agreed that "contextual" should be part of the spec. I'll update that. + +I think the questions you're referring to all are about the direction of maybe considering metadata names, or looking up names, or getting fully qualified names, or fleshing out nameof(type) into a more fully-featured feature. For picking source names e.g. "foo" in your example above, the LDM's opinion was a judgment call that the benefits of a simple language feature in this respect (one that returns the identifier of the argument as you wrote it) outweighed other concerns, and there indeed aren't even good scenarios to guide either way. As for extended the feature to also do fully-qualified names - yes it would unlock scenarios, but again it's a judgment call. Is hard to justify any additional complexity here given the reasonable and nice workaround "typeof(C) & nameof(C.f)". + + + +--- + +On Nov 11, 2014 at 5:18 PM @dougbu wrote: + +@lwischik wrote: +> We also have to drastically re-spec the C# behavior of nameof for extension methods. That's because ... + +Agree the `base` / `this` / plus extension methods semantics described in "IDE behavior" aren't part of "Semantics". That may make sense because the compiler mainly needs to know that an a non-empty method group exists; only the IDE cares exactly which methods are in a method group. + +However at least the 5.0 C# specification doesn't clearly include extension methods in method groups. E.g. section 7.6.5.2 "Extension method invocations" doesn't mention method groups at all. Suggest expanding the existing sections rather than adding more words to the `nameof` section(s). + + +> I think the questions you're referring to all are ... + +Perhaps. I read them as variants of "Why include sub-feature X at all?" For example in what scenario is it useful to have a `const string` for an alias name? + +--- + +On Nov 13, 2014 at 2:31 PM @Ollka wrote: + +**lwischik wrote:** +> **Ollka wrote:** +> > [For C#, it should continue to disallow dotting static member of an instance] +> > Is it error if method-group contains both static and instance methods and qualifier expression is an instance? +> +> That's a good question. I'll have to think about where that sits in the spec. What do you reckon? + +I would suggest do nothing in this case. If any non static candidate exists in method group. + +``` +public class C +{ + public void Foo(string s) { } + public static void Foo() { } + + public void Test() + { + var result = nameof(this.Foo); + } +} +``` + +If compiler would show an error then in all such cases it is impossible to use access of an instance , only static 'C.Foo'. +The other way is to filter out static candidates from the method group. But then if person (or tool) changes staticness of some method it could change the meaning of nameof method group. + +--- + +On Nov 13, 2014 at 5:09 PM @lwischik wrote: + +My opinion for methods is more or less: + +_Given an instance "x":_ + +For all methods "f", if there exist a set of type arguments and method arguments (a1,...) such that x.f(a1...) binds to the method, then that method should be amongst the targets of nameof(x.f). + +_Given a type T:_ + +As above, but based on whether default(T).f(a1...) binds to the method + + + +--- + +On Nov 13, 2014 at 6:31 PM @nmgafter wrote: + +**lwischik wrote:** +> My opinion for methods is more or less: +> +> _Given an instance "x":_ +> +> For all methods "f", if there exist a set of type arguments and method arguments (a1,...) such that x.f(a1...) binds to the method, then that method should be amongst the targets of nameof(x.f). +> +> _Given a type T:_ +> +> As above, but based on whether default(T).f(a1...) binds to the method + +I agree with this as a language design principle. However the specification needs to be _constructive_. The "if there exists" is not directly implementable in the compiler, and cannot be evaluated by the reader of the program. We need a simple rule that can be evaluated to tell us which methods are included. + +--- + +On Nov 13, 2014 at 8:32 PM @lwischik wrote: + +Another TODO item: +https://roslyn.codeplex.com/workitem/370 - describe the "contextualness" rules for nameof... + +It will be similar to what's described in C# spec $2.4.3 for var: "In other cases, such as with the identifier “var” in implicitly typed local variable declarations (§8.5.1), a contectual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword." + + + + +--- + +On Nov 14, 2014 at 2:12 AM @PauloMorgado wrote: + +**lwischik wrote:** +> Another TODO item: +> https://roslyn.codeplex.com/workitem/370 - describe the "contextualness" rules for nameof... +> +> It will be similar to what's described in C# spec $2.4.3 for var: "In other cases, such as with the identifier “var” in implicitly typed local variable declarations (§8.5.1), a contectual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword." + +Only if there's a type named `var`, right? Because that would be ambiguous, given the intent of keeping retro compatibility. + +That's not the case with https://roslyn.codeplex.com/workitem/370 + +--- + diff --git a/meetings/2014/README.md b/meetings/2014/README.md new file mode 100644 index 0000000..ddf9f94 --- /dev/null +++ b/meetings/2014/README.md @@ -0,0 +1,82 @@ +# Visual Basic .NET Language Design Notes for 2014 + +Overview of meetings and agendas for 2014 + + +## Feb 17, 2014 + +[Visul Basic .NET Design Meeting Notes for Feb 17, 2014](LDM-2014-02-17.md) + +a complete roll-up of all language features that are on the table + + +## Feb 2, 2014 + +[Visul Basic .NET Design Meeting Notes for Feb 17, 2014](LDM-2014-02-17.md) + + +Strict Module + + +## Apr 1, 2014 + +[Visul Basic .NET Design Meeting Notes for Apr 1, 2014](LDM-2014-04-01.md) + +String interpolation + + +## Apr 2, 2014 + +[Visul Basic .NET Design Meeting Notes for Apr 2, 2014](LDM-2014-04-02.md) + +Null-propagating operator ?. + + +## Apr 3, 2014 + +[Visul Basic .NET Design Meeting Notes for Feb 17, 2014](LDM-2014-04-03.md) + +ProtectedAndFriend access modifier, and faster CInt(.) + +## Apr 16, 2014 + +[Visul Basic .NET Design Meeting Notes for Apr 16, 2014](LDM-2014-04-16.md) + +#Disable Warning, and ambiguity from unrelated interfaces + +## Apr 23, 2014 + +[Visul Basic .NET Design Meeting Notes for Apr 23, 2014](LDM-2014-04-23.md) + +primary constructors, and pattern-matching. + +## Jul 1, 2014 + +[Visul Basic .NET Design Meeting Notes for Jul 7, 2014](LDM-2014-07-01.md) + +#Disable Warning revisited + +## Oct ,1 2014 + +[Visul Basic .NET Design Meeting Notes for Oct 1, 2014](LDM-2014-10-01.md) + +rules for assignment to readonly autoprops in constructors, and preventing outsiders from implementing your interfaces + +## Oct 8, 2014 + +[Visul Basic .NET Design Meeting Notes for Oct 8, 2014](LDM-2014-10-08.md) + +revisit "ambiguity from unrelated interfaces" + +## Oct 15, 2014 + +[Visul Basic .NET Design Meeting Notes for Oct 15, 2014](LDM-2014-10-15.md) + +revised nameof operator + + +## Oct 23, 2014 + +[Visul Basic .NET Design Meeting Notes for Oct 23, 2014](LDM-2014-10-23.md) + +revised nameof operator again. (Also some breaking changes for ambiguous unrelated interface members) \ No newline at end of file diff --git a/meetings/LDM-2015-01-14-VB.md b/meetings/2015/LDM-2015-01-14-VB.md similarity index 100% rename from meetings/LDM-2015-01-14-VB.md rename to meetings/2015/LDM-2015-01-14-VB.md diff --git a/meetings/2015/README.md b/meetings/2015/README.md new file mode 100644 index 0000000..ce4170d --- /dev/null +++ b/meetings/2015/README.md @@ -0,0 +1,11 @@ +# Visual Basic .NET Language Design Notes for 2015 + +Overview of meetings and agendas for 2015 + + +## Jan 14, 2015 + +[Visul Basic .NET Design Meeting Notes for Jan 14, 2015](LDM-2015-01-14.md) + +Overload Resolution for String Interpolation + diff --git a/meetings/LDM-2016-05-06-VB.md b/meetings/2016/LDM-2016-05-06-VB.md similarity index 100% rename from meetings/LDM-2016-05-06-VB.md rename to meetings/2016/LDM-2016-05-06-VB.md diff --git a/meetings/2016/README.md b/meetings/2016/README.md new file mode 100644 index 0000000..51ddd00 --- /dev/null +++ b/meetings/2016/README.md @@ -0,0 +1,11 @@ +# Visual Basic .NET Language Design Notes for 2016 + +Overview of meetings and agendas for 2016 + + +## May 5, 2016 + +[Visul Basic .NET Design Meeting Notes for May 5, 2016](LDM-2016-05-06.md) + +1. ByRef Returns +2. Tuples diff --git a/meetings/2017/README.md b/meetings/2017/README.md new file mode 100644 index 0000000..87d6066 --- /dev/null +++ b/meetings/2017/README.md @@ -0,0 +1,4 @@ +# Visual Basic .NET Language Design Notes for 2017 + +Overview of meetings and agendas for 2017 + diff --git a/meetings/README.md b/meetings/README.md new file mode 100644 index 0000000..d36dcdc --- /dev/null +++ b/meetings/README.md @@ -0,0 +1,33 @@ +# Visual Basic .NET Language Design Meetings + +Visual Basic .NET Language Design Meetings (LDM for short) are meetings by the Visual Basic .NET Language Design Team and invited guests to investigate, design and ultimately decide on features to enter the Visual Basic .NET language. It is a creative meeting, where active design work happens, not just a decision body. + +Every Visual Basic .NET language design meeting is represented by a meeting notes file in this folder. + +## Purpose of the meetings notes + +Meeting notes serve the triple purposes of + +- recording decisions so they can be acted upon +- communicating our design thinking to the community so we can get feedback on them +- recording rationale so we can return later and see why we did things the way we did + +All have proven extremely useful over time. + +## Life cycle of meeting notes + +- If upcoming design meetings have a specific agenda, there may be a meeting notes file with that agenda even before the meeting happens +- After the meeting notes will be saved directly here. +- Usually they will be raw notes in need of subsequent cleaning up. If that's the case, they will be clearly marked as such, and a work item will track the task of cleaning up the notes. +- When the notes are finalized, a notification is sent to the mailing list. Discussion can happen there. +- If the notes impact current proposals, work items will track updating those proposals. +- When updated, the proposals link back to the meeting where the proposal was discussed. + +## Style of design notes + +The notes serve as the collective voice of the LDM. They cover not just the decisions but the discussion, options and rationale, so that others can follow along in the discussion and provide input to it, and so that we don't forget them for later. + +However, *the notes are not minutes*! They *never* state who said what in the meeting. They will occasionally mention people by name if they are visiting, provided input, should be collaborated with, etc. But the notes aim to represent the shared thinking of the room. If there's disagreement, they will report that, but they won't say who wants what. + +This approach is intended to reinforce that the LDMs are a safe space, and a collaborative, creative effort. It is not a negotiation between representatives of different interests. It is not a voting body, and it is not a venue for posturing. Everybody cooperates towards the same end: creating the best language for today's and tomorrow's Visual Basic .NET developers. +