Open xieguigang opened 6 years ago
@xieguigang
I'm not sure that much of VB's design philosophy was about programming for beginners, especially not VB.Net.
But what I do think is one of the most important aspects of VB.Net is that it encourages legible programming - code that is easy to decode, and easy to maintain.
Those concepts are very subjective, so for example, you suggest that
' Will change this long lambda
Dim fib As Func(Of Integer, Integer) = Function(x) If(x <= 1, x, fib(x - 1) + fib(x - 2))
' to more natural language style for describ an algorithm
' We can apply this default value feature to make this math lambda
' more closer to natural language in VisualBasic
'
' using user custom assert: If x <= 1
Dim fib(~x) = (fib(x - 1) + fib(x - 2)) Or x As Default If x <= 1
I find your short version even more difficult to read and interpret than your 'long lambda'. I would argue that it would be easier to understand what the code is doing if you just used an actual function, which you could comment as appropriate, and more easily add guard clauses later on as necessary:
' Returns the (x + 1)'th number in the Fibonacci sequence 0,1,1,2,...
Function Fibonacci(n as Integer) as Integer
' Fibonacci is only valid for non-negative integers, and 0 and 1 are predefined
' TODO? throw an exception if a negative 'x' is supplied?
' this will return the input for negative numbers, 0 or 1.
If x <= 1 Then Return x
' Return the sum of the two predecessors
Return Fibonacci(x-1) + Fibonacci(x-2)
End Function
The performance of this function will be no different than either of your examples, but it will be easier for the programmer who comes after you to understand what's going on.
I guess that it might make me old-fashioned, but I'm not a fan of gratuitous use of lambdas/anonymous methods - in my experience, they're a right royal pain to debug if there's a problem, with the only benefit being fewer keystrokes when you initially write the code - there is certainly no performance benefit to either of the examples you described over just writing a function, and then there's the additional maintenance effort two years later when you first have to 'decode' your trivial but anonymous code...
I do worry sometimes that some 'beginner' programmers think that fewer characters equates to better performance, even though the opposite is often true (I also find myself wondering if this is part of the reason that C-esque languages are favoured over VB).
@xieguigang
going back to your 'default' suggestion. While I like the concept, I did not find your overloaded use of 'Or' to be easy to follow, and I suspect you may have demonstrated potentially breaking changes.
Because a large part of my time is spent on SQL queries I find myself missing SQL constructs when I find myself in VB. In this case, it's COALESCE, I'd rather favour a more general purpose empty/null/nothing coalescing construct - an extension of If(), if you like, where the result is the first non-null value:
With coalesce(App.GetVariable(NameOf(R_HOME)), R_HOME)
Return Me.SaveTo(coalesce(path, _filePath, App.GetVariable("DefaultFilePath"),"FallBack.txt"))
To be really cool, it would have to tackle strings using IsNullOrWhiteSpace.
I'm not attached to the name 'Coalesce', but once you understand the concept, it is really easy to read.
If I understand correctly, @xieguigang is talking about falsey-coalescence, like we can express concisely in JavaScript: someVariable || defaultValue
Which VB partially has with the binary If expression: If(someVariable, defaultValue)
, but @xieguigang has two problems with this:
If
checks to see if it's "a reference or Nullable value that is not Nothing" -- it should also check that it isn't the default value for that type (i.e. empty string)If(expression, defaultValue)
is not Fluent enough as you have to backtrack to add the If(
So @xieguigang is asking for an Or
keyword that would work more similarly to JavaScript's ||
operator. expression Or defaultValue
.
I do agree with the need for this. However, I propose using OrDefault
as a keyword; it is more explicit as to what is happening, and also doesn't conflict with the existing Or
operator.
(Additionally @xieguigang proposes allowing to pass a custom test expression instead of simply checking for default value. However, I don't have a good syntax for this.)
I just want to add that this can currently be mostly solved with an extension method:
<Runtime.CompilerServices.Extension>
Public Function [Or](test As String, defaultValue As String)
Return If(String.IsNullOrWhiteSpace(test), newValue, test)
End Function
Then you can simply do myString.Or("Default Value")
, which is very Fluent.
You can add overloads for each primitive type, as well as a generic overload for Object types that checks for null. You can also easily add a third parameter there for a lambda as a custom test.
However, this doesn't short-circuit if the test fails (i.e. the expression passed as defaultValue
will be evaluated regardless -- as VB doesn't currently have Scala's call-by-name feature).
Yes, @bandleader get the point
The javascript allows user using operator ||
for set the default value, example as:
eatFruit = function(fruit) {
fruitToEat = fruit || "strawberry";
// ...
}
The R language is also can using a custom operator %OR%
for set the default value, example as:
eatFruit = function(fruit) {
fruitToEat = fruit %OR% "strawberry";
# ...
}
As current VB language not allow set default value to the non-primitive type, so that we can not write a function like this example:
Function eatFruit(Optional fruit As fruit = New strawberry())
End Function
By using the default expression, that we can
Function eatFruit(Optional fruit As fruit = Nothing)
fruitToEat = If(fruit, New strawberry())
fruitToEat = fruit Or New strawberry() ' [As Default] [If fruit Is Nothing]
' ...
Call PeopleEat(fruit Or New strawberry())
' compare with
Call PeopleEat(If(fruit, New strawberry()))
End Function
using fruit Or New strawberry()
is more fluent than If(fruit, New strawberry())
.
@xieguigang How you do like fruit.Or(New strawberry())
, as I mentioned above?
(Because then we could simply add it into the class library instead of a VB language feature.)
Given that we're discussing VB. Would not OrElse be a semantically better choice than Or ?
(Since the | of C-esque languages maps to 'Or', whereas || maps to 'OrElse')
Hi, @bandleader, @pricerc ,
Using an extension method can achieve the goal, like this generic default value function demonstrated:
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function [Or](Of T)(test As T, [default] As T, Optional assert As Func(Of T, Boolean) = Nothing) As T
Return If(Not assert Is Nothing, If(assert(test), test, [default]), If(test, default))
End Function
But the extension method way have a apparent drawback:
The function parameter value isn't lazy, which means we must create the default value at first, then we are able to using the extension function. We may face a awful performance issues if the program takes a long time to create the default value...
@pricerc In my opinion, using Or
will makes the VB code more closer to natural language than using the keyword OrElse
.
:yum:
@xieguigang But the extension method way have a apparent drawback: The function parameter value isn't lazy
Yes, I agree (and wrote that myself, above). Was just pointing it out. I do like the idea of a VB-native ||
.
(Note that we could instead just add Scala's call-by-name for parameters -- but doubt that will happen)
@xieguigang In my opinion, using Or will makes the VB code more closer to natural language than using the keyword OrElse.
But this is a short-circuiting operator, like OrElse
.
In any case, as I said, I don't think it makes sense to either of them (Or
nor OrElse
) for this one, as it's unclear and also creates ambiguities. I proposed (above) using OrDefault
as the keyword -- it's explicit and clear as to what is happening, and also doesn't conflict with anything.
To be clear, C#'s default
expression is literally the same as VB's Nothing
literal, so the first ask: "Optional parameter default value for non-primitive type that can not declare as constant" is solved--you're supposed to write = Nothing
.
As for the second problem, the natural read/write order sounds like the high order problem you're proposing be solved.
LDM discussed this and
This discussion does not seem to have reached a consensus, so leaving open for discussion.
@AnthonyDGreen @KathleenDollard I'm not the OP, but I think the focus was checking for falseyness, not just nullness. The suggestion was for a falsey-coalescing operator, not a null-coalescing operator.
Nothing is not exactly the same as CType(Nothing, SomeType) which I find all over my code base. Nothing will not work in the example below. I left out the "If" to keep example short.
Node1 = Node1.ReplaceToken(LastToken, CType(Nothing, SyntaxToken))
There is a default value expression feature in
C#
language to produce default value for corresponding type. VB language is also have aDefault
keyword but it is working for the indexer property.For the optional parameter in a function that its type is non-primitive, we can not declare it as a constant for set a default value. We usualy set its default value to
Nothing
and then using anIf()
expression orIf ... Then
statement to set the default value. But this have some drawbacks,As @AnthonyDGreen comment that:
Using the
If()
expression actually can caused such problem: break our thinking while we programming. TheFluent design
language feature likeExtension
method can reduce or eliminate the backtracking. There are proposals that in a common theme about the Fluent design in VB language:Proposal in this issue using default value expression for solving:
One of the VisualBasic language feature that people can distinguish VisualBasic with other programming language is that VB language is English words based (C family language like
C#
is symbols based). As one of the philosophy of VB language design is to design a programming more close to natural language for those beginers in programming. So proposal this new VB default value expression in syntax like:Or ... As Default If ...
.The default value expression is a very common scenario, example as this code example list showns:
With App.GetVariable(NameOf(R_HOME)) Or R_HOME
Return Me.SaveTo(path Or _filePath)
Dim out$ = (args <= "/out") Or ([in].TrimSuffix & "_" & name & ".vb").AsDefault
With dev Or Console.Out.AsDefault
csv = Xlsx.Open(.ByRef).GetTable(sheetName:=args("/sheet") Or "Sheet1")
Dim mzGroup = data.GroupBy(Function(d) d.mz, equals:=AddressOf (tolerance Or ppm50).Assert)
Dim css$ = (schema Or Schema.VisualStudioDefault).CSSStyle
Call fs.WriteAllText(path, text Or EmptyString, append, encoding Or UTF8)
And more example is not listed.
Proposal
Or ... As Default
Syntax
Improvement
If
expressionDrawbacks
People may be confused this feature with the
Or
logical operator, but this can be eliminated by using()
bracket pair.Default assert
The
If
expression can be ommited if usingdefault(type)
mechanism or using user custom expression.Using
default(type)
assertThe If expression assert can be ommited in this
Or ... As Default
expression, If the assert is aC#
default(type)
assertion:User custom
If assert
for the default valueFibonacci numbers example:
Factorial example: