IIF, a True Ternary Operator and Backwards Compatibility

One of the things we’ve been discussing for VB 9.0 is adding a true ternary operator to the language. It’s been a persistent source of annoyance for myself (and many others, to judge from suggestions and complaints we’ve gotten over the years) that there is no short-circuiting conditional expression operator in the language. True, there’s the IIF method which does most of what you want, but it doesn’t short-circuit. If you evaluate “IIF(x Is Nothing, 10, x.Foo())” and x is Nothing, then you’ll get an exception because we always evaluate all of the arguments of a method call (since IIF is just a regular method). In contrast, the C-style language’s ternary operator (i.e. “?:”, as in “x == null ? 10 : x.Foo()”) does short-circuit and it comes in mighty handy.

The annoyances get even worse with the introduction of queries into the language, because now there are a lot more places where you want to do in-line conditionals (since there aren’t statements in queries). In fact, I was a huge user of IIF back when I worked on the Access query designer in my previous life. So with LINQ coming, it’d be really nice to have a short-circuiting ternary operator available. So we’ve been pondering how best to do it. 

Rather than introduce some new operator, we’re considering doing to IIF what we did to AscW and ChrW and turning it into an intrinsic function. So even though IIF will still appear to be a function call, we’ll intercept the call and turn it into a true short-circuiting ternary operation. The nice thing is that you won’t have to learn any new syntax–things will just start working the way you expect them to. Probably 99% of programmers will never notice the change or like it. However, there probably will be that 1% of programmers (or maybe .1% of programmers or .01% of programmers) who will notice the change and be unpleasantly surprised. Perhaps you intentionally or unintentionally depended on the fact that all arguments to IIF would be evaluated, regardless of the value of the conditional expression. If so, when you recompile your application in VB 9, your program behavior will break or, even worse, silently change.

So what to do? Well, the language spec does say that we reserve the right to break compatibility for new features “only if the impact would be extremely minimal and the benefit of the feature is high.” I think, based on feedback we’ve collected, the second part of the test is no problem. The first part is the question. For example, we introduced some small compatibility breaks in the language when we added support for unsigned types (that might cause some method calls to bind to different overloads than before), but no one to date has ever reported an issue. So that’s a case where a new feature had a minimal compatibility impact and a high benefit. But what about this case?

Well, we’d like to know what people think. Our take on it is that it is extremely unlikely that anyone is consciously depending on the evaluation of both branches of the IIF expression. Extremely unlikely. And that anyone who’s unconsciously depending on the evaluation of both branches of the IIF expression is actually having a bug in their program covered up by a limitation of the language. If this is the case, then changing the behavior of IIF should help most people write more correct programs and should negatively impact very few people, if any at all.

Or will it? What’s your take?

I should also note that IIF currently doesn’t do the cute typing rules that you’d like from a ternary operator — that is, the return type of IIF is Object, so you have to cast in a lot of cases where you really shouldn’t have to. We’d also fix this at the same time (although we could also fix this by introducing a generic IIF method and fixing a few limitations of our generic inference algorithm–currently if a type parameter infers to two or more types, the algorithm fails rather than taking into account the situation where the type are all related to one another).

And one last huge CAVEAT EMPTOR: since we’re talking about pre-release software, do NOT construe this as a promise this feature will make it in to this release. Even though I believe it will get in, surprises always happen. So don’t start counting this particular chicken until you get actual bits that contain the feature. And even then, nothing’s final until we release to manufacturing.

(Bonus question: Why is it the “ternary operator?” It’s a bit of an imprecise name. An operator that takes one operand is a unary operator. An operator that takes two operands is a binary operator. And an operator that takes three operands…? Since “?:” is usually the only operator in C-style languages that takes three operands, the general “ternary operator” is applied to this specific operator. I imagine there are other names for it, this is just the one I’m used to hearing.)

60 thoughts on “IIF, a True Ternary Operator and Backwards Compatibility

  1. Branco Medeiros

    This is great news.

    I completelly abandoned IIF after I migrating from VB6 to VB.Net 2005, due to its ‘non-genericity’ and, er, "full-circuitry" =P.

    I hope these new feature sees the light of day.

    As an aside, maybe the new compiler could generate a warning when it imported a VB 2005 project containing a IIF()…

    Regards,

    Branco.

    Reply
  2. Pingback: @ Head

  3. Martin Soles

    I’ve long wanted Iif to be an operator instead of a function. This would be helpful to those of us needing a conditional type of expression in SQL Server Reporting Services.

    Reply
  4. David

    I’d love it if IIF both short circuited and returned something other than Object. Often I want to do something like IIF(foo Is Nothing, "-", foo.ToString()) as a parameter when calling a method.

    Unfortunately it would probably break some lousy code that’s out there. You could try changing it for a beta version. If there are complaints then generate a warning as Branco suggests or use a new (preferably short) keyword.

    Reply
  5. Jonathan Allen

    Why can’t we just reuse the If keyword?

    If the line begins with If or ElseIf, then it is the original semantics.

    If the line contains if anywhere else, then it is the new semantics.

    Reply
  6. Speednet

    GREAT!!! I’m very happy to hear that you’re going to add a true ternary operator.

    I would rather than you NOT change the behavior of IIf(), considering that it’s been around in its present state for so long.

    The solution is SIMPLE, at least how I see it. Just keep the IIf keyword, but use it as a real operator as follows:

    IIf (x = 1) Then [do something] Else [something else]

    (The parens would be optional — I always put parens around expressions for readability.)

    Reply
  7. Pingback: OPC Diary

  8. Mike Schinkel

    >> However, there probably will be that 1% of programmers (or maybe .1% of programmers or .01% of programmers) who will notice the change and be unpleasantly surprised.

    I’ll notice the change and be unpleasantly surprised, but not for the reasons you assume. Instead, I think it is a really bad language design to create things that look like function calls that do not behave like function calls. Period. It just makes learning the language more difficult, and it breaks orthogonality.

    I think it an operator is a far, far better approach, even if that operator is something like:

    x= Iif foo Then bar Else Baz End

    The cool thing about this is you could also make it do this:

    x= Iif a Then x ElseIf b Then y ElseIf c Then z Else q End

    Reply
  9. Mike Schinkel

    While you are at it, why not add a operator casting? CType() has got to be one of the most unnatural forms of casting I’ve ever seen and using it just feels wrong.

    So instead of:

    c= CType(192, Byte)

    How about?:

    c= Cast 192 As Byte

    Reply
  10. Einar G.

    I would be glad to see this in. But even tough I never used it in the way it will brake I know my partner in crime has used it that way so we be in some kind of trouble. But it would be worth it.

    Reply
  11. stand__sure

    Please by all means change IIf. It should be an intrinsic and return typed values.

    I’ve been doing this for a while…

    Shared Function IIf(Of T)(ByVal expression As Boolean, ByVal truePart As T, ByVal falsePart As T) As T

    If (expression) Then Return truePart

    Return falsePart

    End Function

    …it works, but it doesn’t always inline… so, an intrinsic would be better.

    on syntax, (although I think the "=>" for lambda functions is silly…) I would favor borrowing from the C-style languages on this, to wit: x= (booleanCondition) ? (truePart) : (falsePart) style of syntax…

    the words in a high level language should help to make it readable now and in the future. they should make code readable by humans. No one other than a VB programmer will know what IIf means… the x=b?t:f form is no better, but it is more universal. consequently, more programmers will be able to understand the c-style ternary syntax than the VB IIf form.

    on the "it may break existing code" concern, break the code! Code that relies upon such arcane aspects of the language is in need of a good refactoring anyways.

    Reply
  12. Joe F

    2 great ideas!

    I have avoided IIF in .Net due to the Casting issue.

    Also – I find it hard to imagine programs that rely on the evaluation of both parts of the IIF. Short circuiting is much better. I vote for the breaking change.

    Reply
  13. Anthony D. Green, MCPD

    OOOOOOH, I(nline)If. That’s why it has that name. Never did get it until just now.

    On the matter of Syntax:

    IIf(,,) is the briefest and most familiar

    Iif … Then … Else … is the most descriptive and… VB-like.

    … ? … : … is the more recognizable to the hordes of C-derived programmers who just happen to be deeply using VB.NET, all 2 of them. It’s cryptic, visually, to VB users (regardless of whether we know what it does or not – it’s an inside joke, you know it just because you do, not because it’s discoverable. Kinda like the way half the world is declaring arrays in VB by capacity and not upperbound and suprised to see one too many elements – they don’t get the inside joke).

    On the matter of backward compat. If you go with the syntax where this is an issue then break the backwards compat without hesitation. Only the stupidest of us would have a side effect in such a place. It’s like making your property getter change and object’s state or attaching a trigger to a Select (not that you can). It won’t affect lazily instantiated properties. Not your job to support shear stupidity.

    Simplest solution, go with the Iif Then Else synsax and don’t worry about it. Looks longer but it’s rarely brief to inline a condition and two values which may be function calls which may require parameters. This topic would dovetail nicely into a discussion of other VB language shortcomings related to line-length but this isn’t my blog so I’ll simply finish this entry off.

    Happy New Year to all!

    Reply
  14. Cameron Beccario

    Considering the controversy when VB 7 shipped, I’d say don’t break backwards compatibility when you have the luxury of choice. Perhaps the language spec should say "only if the impact would be extremely minimal, realistically unavoidable, and the benefit of the feature is high."

    In the case of "IIf", you have reasonable alternatives. IIf is an old function and most documentation has documented its shortcomings, so let it die. Mark it obsolete, even. And since it may be redefined by the user elsewhere, waiting until "IIf" binds specifically to MS.VB.IIf before morphing it into short-circuited branches is just odd/surprising. This feature should be syntactical (consider all those home brew parsers out there), so why not instead extend "If" functionality?

    You can already do this in one statement:

    —-If b Then foo() Else bar()

    So why not allow its use as an expression in an expression context?:

    —-Dim result As Boolean = If b Then x + 1 Else x + 2

    One annoyance is that unreadable code is easy to write:

    —-If If b Then c Else d Then foo() Else bar()

    And you have interesting cases where "ElseIf" and "Else If" have different meanings:

    —-Dim result As Boolean = If b Then If c Then x + 1 Else x + 2 ELSE IF d Then x + 3 Else x + 4

    So perhaps you require parens around the If expression:

    —-If (If b Then c Else d) Then Foo() Else bar()

    —-Dim result As Boolean = (If b Then (If c Then x + 1 Else x + 2) Else (If d Then x + 3 Else x + 4))

    Requiring parens to encapsulate an expression already has precedent. Consider the case of suppressing the Set when passing a property to a byref parameter:

    —-Foo((a.Name))

    Or the case of using the member of a newly constructed object:

    —-(New Widget).Go

    Or, maybe you just let users write unreadable code if they want.

    Reply
  15. Michael S. Kaplan

    Change it!

    (trivia — The Jet Expression Service version of IIf already short circuita rather than using the VBA Runtime function to do its work — so they sort of did the same thing VB is think of doing, only 8-10 years earlier? 🙂

    Reply
  16. Pingback: Colin Neller

  17. russ

    We’re a decent-sized VB.Net shop. We’ve got over 42,899 kb of VB.Net source code (that I know of).

    This change will only positively impact us and would be very useful. We run into WAYYYYY more situations wherer a true IIF operator that short-circuits would be useful than the reverse.

    Change away!

    p.s. An alternate syntax like embedding an If in the middle of a line could be combined to do something like this without breaking compatibility if needed:

    myvar = IIf (blah = true) Then truepart Else falsepart

    Either way, find a method to get this into the next version!

    Reply
  18. scott

    Ah, finally, the possibility of true ternary operator. Yes, I would also be in the “change away” camp. I can’t see supporting both the non-ternary version of iff and introducing a new operator for the ternary operation. In the long run that seems like it might be the source of more ambiguity and problems then the price of breaking this particular piece of backward compatibility.

    Reply
  19. Roger Jennings

    As you mention, IIf() is a lifesaver in Jet (now Access) queries and is likely to be the same in LINQ expressions. Short-circuiting would have been very welcome in Jet and I recommend it highly for VB9. I don’t believe IIf() is widely used in VB7 and 8, so the change isn’t likely to affect many existing apps.

    –rj

    Reply
  20. davidG

    Hooray! This has really annoyed me since I started with .net, both the casting requirement and the non-shortcircuiting.

    However, I disagree with retaining the function IIf() and mapping it to the new operator. Mainly for the same reasons I hate CType() (as someone else already pointed out, I’d vote for ‘Cast x As Integer’ too).

    Whether it should be:

    x = If someBool Then 1 Else 2

    or

    x = IIf someBool Then 1 Else 2

    I’m not sure (and don’t really care).

    The people who need this feature will notice it and start using it, and any small percentage of code that requires the old broken version can carry on without any problems. It wouldn’t surprise me if the current IIf() is fairly underused as it is, so moving to a new statement wouldn’t be that painful.

    Reply
  21. Michael

    #1)

    x = IIF true, do something, do something else

    #2) CType

    instead of c= CType(192, Byte)

    what about

    c = 192 as byte

    Reply
  22. Tim

    Out of everything thats been raised i like

    x = If blah then 10 else 12

    and

    c = 192 as byte

    They both seem more VBish .

    The first one doesn’t really require any new syntax or rules to learn, just that Ifs can be expressions (it may be worth considering allowing multi line Ifs to become expressions as well, but i feel its probably best not to, any ternarys should only be used for simple things)

    The 129 as byte screams vb since vb uses the as keyword to declare a type of variable and C# doesnt yet steals the as keyword for easy casting.

    Reply
  23. Justin Michel

    I agree that a short-circuiting IIf(,,) is confusing, because it wouldn’t be possible to implement as a function without special support from the compiler. A function call passed as the third argument would normally be evaluated at the call site. At the very least, if you do use IIf, then make it a keyword, and don’t have it relate in any way to Microsoft.VisualBasic.Interaction.IIf()

    I think the proposed alternative of allowing the existing

    If … Then … Else … to be an expression is confusing.

    Why not improve on the C languages, and line things up so that the default case is easier to read?

    For example, I might have the following:

    If a = b Then

    If I want to change this to compare a to the minimum of b or 5 I could use:

    If a = (b IIf b < 5 Else 5) Then

    Or maybe, b can be null:

    If a = (b IIf b IsNot Nothing Else GetDefault()) Then

    Here’s more examples:

    Foo(a, (x IIf a > 0 Else GetDefault()), z)

    Dim x As Integer = (1 IIf IsLoggedIn Else 0)

    And the orginal C++ example:

    10 IIf x Is Nothing Else x.Foo()

    This could just as easily use "If" as the keyword, but I think it’s nice to have the distinction.

    Reply
  24. Mike Griffiths

    Go with the "short circuit" as there are so many occasions when this will result in less code as well as faster execution.

    Plus, if my observation of other people’s code is anything to go by, this will reduce bugs rather than adding to them.

    Reply
  25. Allan S. Hansen

    Oh please make IIF shortcurcited, or simply adopt the C++/C#+/Java-ish method of writing it ?: if legacy is something to worry about.

    This is one feature I serverly lack when programming VB.Net, the inability to make inline shortcircutted assignment would save me many nested IF’s and lines of code.

    I also think it is easier to read.

    Reply
  26. Bob

    Please make IIF shortcircuited and typed. Do NOT create something new to preserve backwards compatibility for people who abused the language. It still makes me mad all over again at the VB6 crybabies whenever I have to write OrElse or AndAlso.

    Reply
  27. Eden

    I vote yes please change IIF to shortcircuit. I have run into many instances when I wanted the IIF to shortcircuit and was forced into writing lengthy full IF… ELSE blocks to avoid accessing an uninstantiated object. Since I am so comfortable with using IIF I’d also prefer if the syntax remained the same.

    Reply
  28. Jens Samson

    Please change it, all the reasons are already listed here above, and whatever you do, don’t listen to Karl E. Peterson…

    Reply
  29. Michael Irwin

    I don’t think you should change the current IIF – I think you should deprecate it and get rid of it entirely in VB10.

    Instead, how about

    IIF (condition), (F_expression), (T_expression)

    with the commas and parentheses mandatory. Thus the following would be valid:

    IIF (3 > 4), (str="3 > 4"), (str="3 not > 4")

    and printing str gives "3 not > 4".

    IIF (3 > 4), (), (str="3 not > 4")

    and printing str still gives "3 not > 4".

    IIF (8 > 4), (), (str="8 > 4")

    and printing str gives "8 > 4".

    so if you don’t want to do anything in one eventuality you can indicate that by the empty pair of parens.

    Reply
  30. Duncan

    A second vote for get rid of IIF altogether – the only benefit seems to be top reduce the number of key presses the programmer has to press and lazy programmers write bad code – the lazier you allow them to be, the worse their code will be.

    This is doubly true of implied variable typing…

    Reply
  31. DiegoV

    First, I agree that if some program depends on both arguments to be evaluated it is probably a bug.

    However, it would be very nice if the new ternary operator were more consistent with the also new lambda functions.

    Reply
  32. OneNerd

    Why break backward compatibility? There are already too many not moving to .net from vb6 because of this.

    My vote is for introducing another operator (what’s the downside?) instead of breaking compatibility. What’s one more operator (I mean, you certainly can’t call .net a concise language, can you?)

    Don’t break any more existing code, please ..

    Reply
  33. Majd

    Definately do it.

    Short circuiting IIF is long overdue.

    I can’t remember the times I had to avoid using IIF only because of its evaluation of both parts. Especially in an object oriented programming language, where we need to make sure an object is instantiated before calling a function or retrieve a property. The following pattern is ubiquitous in C#, but can’t be used presently in VB.Net for that reason:

    res = obj == null ? null : obj.someproperty;

    This would be possible in VB.Net:

    res = IIF(obj Is Nothing, Nothing, obj.someproperty)

    Instead of the current pattern:

    If(obj Is Nothing)

    res = Nothing

    Else

    res = obj.someproperty

    End If

    Reply
  34. VB6 rulez

    No more breaking changes! Introduce a new operator IIf2() with the desired short-circuiting and typing. Keep the old IIf() the way it is. This way you get all the benefits.

    Reply
  35. Stephan

    I definitely support breaking the old behavior. I used to use IIf in VB6, but with the advent of VB.NET and strong typing with generics, IIf is almost completely useless.

    The current, non-ternary behavior is also problematic. After the OrElse and AndAlso operators were introduced, I use them exclusively in order to reduce nested Ifs or Select Cases.

    Reply
  36. Chuck

    I vote to introduce a new operator with the desired functionality. If that isn’t possible, then go with the breaking change. The increased productivity going forward is worth the minor refactoring that will be necessary.

    (I also use the heck out the OrElse and AndAlso operators.)

    Reply
  37. Andrew

    please please please add this!

    Go ahead and use any operator you like, but if you can get away with it you should use the "?:" syntax. It’s pretty much universal, and vb’s crybaby community should not get to dictate everything. Just tell them it comes from python or ruby, not c, and they’ll probably accept it with glee.

    Otherwise I assume it will be a vb-ish, confuzed every-word-capitalized version of english, but it will still work just as well and we’ll all kiss your feet for FINALLY adding this.

    Reply
  38. Michael Fitzpatrick

    Consider: AndAlso and OrElse

    Why is necessary to reuse the word IIF? MS didn’t do in for And and Or, so why do it for IIF. Its easy enough to create a new word for the ternary operator. The only code that breaks is the code that is using the new keyword as an identifier.

    Reply
  39. Chris

    I love the idea of adding a true ternary operator to the language. I also love the idea of making IIF work better. However, when a new version of a language changes the way it works, it becomes that much more difficult to maintain old code. I say this because I’ve had to maintain old code, and have had to work with the different versions of VBA that have come out in the past years. Anyone who has learned VB, then had to program VBA in Access knows my pain. Please, don’t change existing functions or operators. I like to know what to expect when maintaining old code. I don’t like discovering after 3 hours of research and head-scratching that "Oh, it doesn’t work the same way in this version of the language." Today we know what to expect when we use IIF. Why change our expectations? Why not introduce a new operator that everyone knows will short-circuit, and every search engine reports that it does just that?

    Reply
  40. Anthony Wykle

    I have been programming in Microsoft BASIC since gwbasic, basica, and qbasic. I loved VB 5.0 when it released, eagerly looked forward to VB 6.0 when the opportunity to acquire it arose, have worn out a keyboard typing VB.Net code, and am now working with VB 2005 Express and loving it even more. There has, however, always been one small problem. I also work with C, C++, Java, JavaScript, etc. All of these languages incorporate the ?: ternary operator. BASIC, for all its advantages, is still very basic when handling Iif(), and I think that, in addition to making Iif an intrinsic and causing it to work just like ?:, it would be acceptable and intelligent to add the ?: operator, providing the same functionality, because, in my opinion, although many dedicated programmers of VisualBasic applications may not know enough C-type syntax to even ever discover ?:, those of us who do understand that (condition)?(trueStatement):(falseStatement) is a very clear and concise way to write code, while Iif(condition, trueStatement, falseStatement) has some very big problems with readability for anyone new to the language. And by all mains make it short circuit. I have had code break and stay broken for days because I misused Iif coming off a C++ or Java session. The short circuiting method is much more intuitive.

    Reply
  41. David Anton

    I’d strongly recommend against the "?:" syntax for VB. It just goes against the grain in VB to use this syntax. It’s in the spirit of VB to use English syntax rather than special symbols. The same short-circuiting operator behavior can be achieved with some combination of If/Else:

    foo = TrueValue If SomeCondition Else FalseValue

    or:

    foo = If SomeCondition TrueValue Else FalseValue

    etc.

    Reply
  42. Reinier

    My vote for

    – having the ? : equivalent by all means (I’d be using it a lot)

    – VB.NET-like syntax (If c Then e1 Else e2 EndIf, or same structure with other keywords). no commas, brackets or anything. there are reasons I prefer VB.NET over C#!

    – not breaking backward compatibility (leave the existing IIF alone, with its FORTRANesque syntax)

    – choosing something that won’t confuse newbies

    (perhaps using the If or Iif keyword is out for that reason; well, use InCase instead or something)

    Reply
  43. Vano

    1. New ternary operator behavior is what we all need.

    2. IMHO, keyword should differ (IFF is what I liked :), but syntax should be left the same, because it is actually optimal: "If condition Then t Else f" is too verbose and big and C-style is not readable. Thus, "If(condition, t, f)" is the best IMO.

    Reply
  44. Patrik Hägne

    To me it seems that the best solution would actually be two solutions.

    Keep the IIF method but make it a generic method so that no casting is needed, this would ensure total backward compatibility.

    Implement ternary functionality for the if-operator like "x = If condition Then True Else False" this would ofcourse be using shortcut evaluation.

    Reply
  45. Pingback: Panopticon Central

  46. Waiting for VB10

    What happened to this big bubble?? We’d all been full of hope, and then… duh!!! Nothing has changed, and VB9 came with the old useless IIF. For god sake, is there any reason for that? I’m a big VB fan, and hate the silly C alike syntax that’s short but not readable; nevertheless I’ve never used IIF because it is exactly useless for professional programming. But today I found myself forced to use it for the first time in my life, and it broke as expected because it is still in the old fashion (i.e. function) and has not been changed into an operator as I thought. Where I’ve had to use it, you may ask? Duh, here:

    In the latest ASP.NET, we’ve got declarative data binding using # (e.g. <%# Eval("fieldname") %>). As far as I know, we cannot use the normal if.then statement in this binding expression, thus we’ve got to use IIF, and no choice for someone like me to avoid it anymore. Well, as you said, this will break in many cases, let aside the performance cost of evaluating things that are not needed repeatedly when binding a data-grid for example. Someone might comment: but you can call a function in <%#…%> thus you can do whatever you like. Well my friend, of course this is what I’ve been doing so far, but can you think a bit further than your nose? Do you remember a fancy announcement about ASP.NET and the separation between the UI and code? As a general rule, the UI should depend as little as possible on the code-behind, and more importantly, we should try to avoid putting some UI in the code-behind. Example:

    To format the value of a data-field:

    <p>Price: <%#IIf(IsNumeric(Eval("Price")), FormatCurrency(Eval("Price").ToString), "N/A")%></p>

    Of course the IIF will break -as you know- so the solution is to create a function that formats the price, and call it instead of IIF. But what if my data-bound control (let’s say datalist) has many fields that need to be formatted in different ways? Then I will be creating so many functions and calling them from the UI only because the stupid IIF is misbehaving (actually behaving correctly, but in the old useless fashion). Isn’t this insane? While look at the example above how elegant it is if the IIF works in the logical and common-sense manner, as an operator? So now we’re waiting for VB10!!!

    Reply
  47. Daniel Sampson

    In my opinion the IIF Ternary Operator should be short-circuited. It was no end of annoyance having a program fail because the False part could not be evaluated… Even though the Condition was True!

    Reply
  48. Yisman

    Yup, were all waiting for a true shortcircuited IIF. its long overdue! this article is 2 years old and were still waitin in the cold…

    thanks

    Reply
  49. Pingback: Anonymous

Leave a Reply