-
Aug 17th, 2021, 03:16 PM
#321
Re: TwinBasic
Originally Posted by Krool
My proposal would be LetOrSet statement.
Thus the statement tells already whats going on. Either a Let or Set statement. (Though Let is optional and seldom used)
LetOrSet works for me!
-
Aug 17th, 2021, 03:20 PM
#322
Re: TwinBasic
Originally Posted by wqweto
Is it possible to declare Private g_myField As T instead of Variant?
You must have missed the comment in my code. I said that the TwinBASIC compiler complains about that:-
I'm guessing that the TwinBASIC compiler expands generics into non-generic classes of each type based on what types you have defined in the application. In this case, when it expands into a String type, the compiler complains about Set being used to assign a String. It would be the same as doing this:-
Code:
Private Class OtherClass(Of T)
'Same as using T since we have declared String versions of this class
Private g_myField As String
Public Sub SomeMethod(ByVal value As T)
if isobject(value) then
set g_myfield=value
else
g_myfield=value
End If
End Sub
End Class
It only works for all types if the field is a Variant.
Last edited by Niya; Aug 17th, 2021 at 03:25 PM.
-
Aug 17th, 2021, 03:52 PM
#323
Re: TwinBasic
Originally Posted by Niya
It only works for all types if the field is a Variant.
Yes, the if isobject(T) then part has to be executed at compile time and act like a preprocessor so that the invalid statements are not compiled at all.
The idea is for LetOrSet to wrap this comptime logic but a more universal solution has to be researched/borrowed from .Net or C++ depending on which of these is closer as generics implementation (probably .Net).
Btw, using As Variant instead of As T defeats the idea of generics. Currently built-in Collections use Variant as a way of duck-typing it. The idea of ListOf(T) is to not only expose strongly typed Item (indexed) property but to use an array of pointers to T internally for performance/storage reasons too.
cheers,
</wqw>
Last edited by wqweto; Aug 17th, 2021 at 04:00 PM.
-
Aug 17th, 2021, 04:53 PM
#324
Re: TwinBasic
Originally Posted by Krool
My proposal would be LetOrSet statement.
Thus the statement tells already whats going on. Either a Let or Set statement. (Though Let is optional and seldom used)
What about Assign?
-
Aug 17th, 2021, 04:54 PM
#325
Re: TwinBasic
I just figured out a way to avoid the TwinBASIC compiler and allow me to use type T as a field:-
Code:
Private Class OtherClass(Of T)
Private g_myField As T
Public Sub SomeMethod(ByVal value As T)
If isobject(value) Then
'We have to do a song and dance to avoid the TwinBASIC compiler
'***********************************************************
dim objPtr as longptr
'Get object pointer
CopyMemory(varptr(objptr),varptr(value),4)
'Call AddRef on the object through its pointer
AddRef(objPtr)
'Dump the object pointer directly into the class
'member field
CopyMemory varptr(g_myfield),varptr(objptr),4
Else
g_myField = value
End If
End Sub
private sub AddRef(byval objPtr as longptr)
'Roundabout way of calling AddRef
dim obj as object=PtrToObject(objptr)
dim zero as long=0
'Prevents TwinBASIC from decrementing the reference count
'when this sub returns
CopyMemory VarPtr(obj), VarPtr(zero), 4
End Sub
End Class
-
Aug 17th, 2021, 06:31 PM
#326
Re: TwinBasic
Here's a version using wqweto's method from post 315:-
Code:
Private Class OtherClass(Of T)
Private g_myField As T
Public Sub SomeMethod(ByVal value As T)
If isobject(value) Then
'We have to do a song and dance to avoid the TwinBASIC compiler
'***********************************************************
dim objPtr as longptr
'Get object pointer
CopyMemory(varptr(objptr),varptr(value),4)
'AddRef the object by it's pointer and dump that pointer
'into the field
vbaobjsetaddref(g_myfield,objptr)
Else
g_myField = value
End If
End Sub
End Class
-
Aug 17th, 2021, 07:08 PM
#327
Member
Re: TwinBasic
Originally Posted by Eduardo-
What about Assign?
FWIW, I very much prefer Assign.
-
Aug 17th, 2021, 09:19 PM
#328
Re: TwinBasic
Originally Posted by WaynePhillipsEA
Originally Posted by Krool
My proposal would be LetOrSet statement.
Thus the statement tells already whats going on. Either a Let or Set statement. (Though Let is optional and seldom used)
LetOrSet works for me!
How about Let_Set or LetSet (I prefer LetSet)
Edit:
In addition, VBA occupies a very good keyword LSet. If VBA.LSet is not considered, we can merge Let and Set into LSet, and then replace VBA.LSet with other function. Of course, this is just an unrealistic idea.
Edit2:
If LSet cannot be used, maybe we can use SLet. It seems that LetSet is more intuitive.
Last edited by SearchingDataOnly; Aug 17th, 2021 at 09:34 PM.
-
Aug 17th, 2021, 10:26 PM
#329
Re: TwinBasic
I'm really not feeling any of those to be honest. I mean all of it, not just the ones SD mentioned above. I really do not like the idea needing an extra construct for assigning Objects when a normal assignment expression would suffice.
Now since it cannot be removed due to the quirk with Variant assignments mentioned here, I propose.....drum roll please.......Option Set Off
I propose the introduction of a new member to the Option Explicit family. When used it will make Set implicit in the code file whenever an object assignment is performed. With this method we can effectively remove this abomination from TwinBASIC at our convenience while also maintaining full backward compatibility with VB6 since it will be on by default. VB6 projects obviously won't have this option set since it didn't exist in VB6. And as a bonus, we can still get all those fancy keywords you guys suggested above for those of you that actually want this. Personally though, I'd like the option to get rid of that garbage whenever I want. Seriously, using Set makes me want to drive molten nails into my skull. Just my opinion.
Last edited by Niya; Aug 17th, 2021 at 10:50 PM.
Reason: Clarification.
-
Aug 17th, 2021, 10:56 PM
#330
Re: TwinBasic
Originally Posted by Niya
I'm really not feeling any of those to be honest. I mean all of it, not just the ones SD mentioned above. I really do not like the idea needing an extra construct for assigning Objects when a normal assignment expression would suffice.
Now since it cannot be removed due to the quirk with Variant assignments mentioned here, I propose.....drum roll please....... Option Set Off
I propose the introduction of a new member to the Option Explicit family. When used it will make Set implicit in the code file whenever an object assignment is performed. With this method we can effectively remove this abomination from TwinBASIC at our convenience while also maintaining full backward compatibility with VB6 since it will be on by default. VB6 projects obviously won't have this option set since it didn't exist in VB6. And as a bonus, we can still get all those fancy keywords you guys suggested above for those of you that actually want this. Personally though, I'd like the option to get rid of that garbage whenever I want. Seriously, using Set makes me want to drive molten nails into my skull. Just my opinion.
Like you, I hate the Set keyword. But the question is, how do you deal with the default value of an object? Do you want to tell everyone not to use the default value?
Originally Posted by WaynePhillipsEA
We can't make Set optional since in classic VB it is valid to do things like
Code:
MyVariant = MyObject
where the semantics are different to
Code:
Set MyVariant = MyObject
. In the first version, the default member of MyObject is evaluated (DISPID_VALUE), and in the second version it is not. One of the reasons they were able to remove the Set keyword requirement in VB.NET is because they don't support default member evaluation in VB.NET like classic VB does.
-
Aug 17th, 2021, 11:52 PM
#331
Re: TwinBasic
How about a slightly different operator to control that behaviour instead of LetSet/Assign?
Code:
MyObject1 := MyObject2
-
Aug 18th, 2021, 12:45 AM
#332
Re: TwinBasic
Originally Posted by SearchingDataOnly
How about Let_Set or LetSet (I prefer LetSet)
Edit:
In addition, VBA occupies a very good keyword LSet. If VBA.LSet is not considered, we can merge Let and Set into LSet, and then replace VBA.LSet with other function. Of course, this is just an unrealistic idea.
Edit2:
If LSet cannot be used, maybe we can use SLet. It seems that LetSet is more intuitive.
LSet is not a shortcut for LetSet. There also exist RSet.
So instead it means Left/Right. (for strings)
-
Aug 18th, 2021, 01:03 AM
#333
Re: TwinBasic
Originally Posted by SearchingDataOnly
Like you, I hate the Set keyword. But the question is, how do you deal with the default value of an object? Do you want to tell everyone not to use the default value?
This is why I said it should be a compiler option like Option Explicit, so you have the choice to opt in or not. You will never see me writing code like that. If I want a property value from an object, I will explicitly call the property. I have zero need for Set. It's just an extra mental burden that gives no extra value. Using Set is like paying your taxes and not getting your roads built and repaired.
-
Aug 18th, 2021, 01:09 AM
#334
Re: TwinBasic
Originally Posted by WaynePhillipsEA
How about a slightly different operator to control that behaviour instead of LetSet/Assign?
Code:
MyObject1 := MyObject2
I like this option best. It makes a lot more sense and it's less verbose.
EDIT:
Also, what would be even better is if := behaves like a normal assignment when it's not assigning an Object. People like me could default to using it for most assignments. I like consistency. I don't like having to always think about how I'm performing an assignment.
Also, I've been wondering. Is the Variant/Default property thing the only reason Set cannot be removed?
Last edited by Niya; Aug 18th, 2021 at 01:20 AM.
-
Aug 18th, 2021, 01:30 AM
#335
Re: TwinBasic
Originally Posted by Niya
I like this option best. It makes a lot more sense and it's less verbose.
EDIT:
Also, what would be even better is if := behaves like a normal assignment when it's not assigning an Object. People like me could default to using it for most assignments. I like consistency. I don't like having to always think about how I'm performing an assignment.
Also, I've been wondering. Is the Variant/Default property thing the only reason Set cannot be removed?
The proposed := assignment would behave as earlier discussed about the LetOrSet keyword (i.e. if either side is an explicit object, then Set-assignment is used, else defer to Let-assignment).
Off the top of my head I can't think of anything other than default-member evaluation that prevents the removal of the Set keyword. By the way, it's not just the Variant example I gave earlier, there's lots of examples, e.g. 'MyObject1 = MyObject2' is valid in classic VB if there's a property-let default member on MyObject1 (or even a property-get default-member that returns an object that itself has a property-let default member)... it's a minefield, and it's heavily baked into the language.
-
Aug 18th, 2021, 01:57 AM
#336
Re: TwinBasic
Originally Posted by Krool
LSet is not a shortcut for LetSet. There also exist RSet.
So instead it means Left/Right. (for strings)
Yes, I know this.
-
Aug 18th, 2021, 02:04 AM
#337
Re: TwinBasic
Originally Posted by WaynePhillipsEA
How about a slightly different operator to control that behaviour instead of LetSet/Assign?
Code:
MyObject1 := MyObject2
Excellent. It is exactly the way I use it in my scripting language (my scripting language has removed the Set keyword)
In my scripting language, ":=" has two uses:
(1) Exempt variable declarations (similar to Golang), for example:
var1 := getMyVaraible '--- var1 does not require variable declaration
(2) Get the default value of an object
myDefaultValue := myObject
Note:
If myDefaultValue is declared, then myDefaultValue is used to get the default value of myObject. If myDefaultValue is not declared, then myDefaultValue is the object reference of myObject.
But this point is easy to cause ambiguity, so I'm considering whether my scripting language retains the feature of the default value of the object.
Last edited by SearchingDataOnly; Aug 18th, 2021 at 02:17 AM.
-
Aug 18th, 2021, 04:17 AM
#338
Re: TwinBasic
How about augmenting the preprocessor to understand comptime expressions so this would compile
Code:
#If isobject(T) Then
set g_myfield=value
#Else
g_myfield=value
#End If
. . . as a generic solution instead of introducing any (piecemeal) language constructs.
Currently VBx preprocessor is able to understand *some* expressions containing arith operators and type conversion functions (CLng, etc.) so adding support for more functions/constructs here is not surprising, provided that generics source code is not expected to be round-tripped to VBx in first place.
cheers,
</wqw>
-
Aug 18th, 2021, 04:33 AM
#339
Re: TwinBasic
Originally Posted by wqweto
How about augmenting the preprocessor to understand comptime expressions so this would compile
Code:
#If isobject(T) Then
set g_myfield=value
#Else
g_myfield=value
#End If
. . . as a generic solution instead of introducing any (piecemeal) language constructs.
Currently VBx preprocessor is able to understand *some* expressions containing arith operators and type conversion functions (CLng, etc.) so adding support for more functions/constructs here is not surprising, provided that generics source code is not expected to be round-tripped to VBx in first place.
cheers,
</wqw>
Unfortunately we wouldn't be able to make that work. Generics work off the parse trees, but the preprocessor strips out code before it even gets parsed.
We would literally need the equivalent to the C++ if-constexpr to make it work like that:
Code:
IfConst isobject(T) Then
set g_myfield=value
Else
g_myfield=value
End If
-
Aug 18th, 2021, 04:56 AM
#340
Re: TwinBasic
Originally Posted by WaynePhillipsEA
. . . but the preprocessor strips out code before it even gets parsed.
How do you syntax highlight code inside branches of the preprocessor #If then?
I see what the problem is but I always hated how C++/Zig implement IfConst like normal expressions with no clear marking what is evaluated at compile time and what not. Much worse than the way #If and the rest of the VB6 (and C/C++) preprocessor directives are obvious and human readable.
cheers,
</wqw>
-
Aug 18th, 2021, 05:14 AM
#341
Re: TwinBasic
Originally Posted by wqweto
How do you syntax highlight code inside branches of the preprocessor #If then?
In tB, we don't, we just grey-out the whole block. In VBx it is slightly different because they have a tokenizer stage that runs before the real parse run, which allows for highlighting of keywords and some syntax errors, but ultimately the code doesn't get to the fully parsed stage. You can see this if you enter illegal code in an #If block that isn't included; VBx will highlight some syntax errors in the tokenizer stage through the IDE (highlighted red), but the parser/resolver/compiler doesn't see them, and compilation can succeed regardless.
-
Aug 18th, 2021, 08:10 AM
#342
Re: TwinBasic
Since Niya brought up the "generics-example"...
I've modified it a bit, to (primarily) point out a different "nice to have" feature
(which would be great, if it was introduced).
Code:
Module modHelloRC6
Public New_c As New cConstructor
Sub Main()
Dim Longs As cArrayList = New_c.ArrayList(vbLong, 1, 2, 3)
Dim Persons As cArrayList = New_c.ArrayList(vbObject, New cPerson("John"))
Dim L As Long '<- tB does "For Each Auto-Coercing from Variant" already, also with simple types
For Each L In Longs
Debug.Print L
Next
Dim P As cPerson '<- this would work also in VBA/VB6, whereas the above "L As Long" wouldn't
For Each P In Persons
Debug.Print P.Name
Next
End Sub
End Module
Private Class cPerson
Public Name As String
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
So, whilst the above Code will work "as it is" in tB already (when an RC6-reference is included) -
I'd like to be able to have "LoopVar-TypeInitializers in For- and For-Each Loops, like below:
Code:
Module modHelloRC6
Public New_c As New cConstructor
Sub Main()
Dim Longs As cArrayList = New_c.ArrayList(vbLong, 1, 2, 3)
Dim Persons As cArrayList = New_c.ArrayList(vbObject, New cPerson("John"))
For Each L As Long In Longs
Debug.Print L
Next
For Each P As cPerson In Persons
Debug.Print P.Name
Next
End Sub
End Module
Private Class cPerson
Public Name As String
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
Olaf
-
Aug 18th, 2021, 09:04 AM
#343
Re: TwinBasic
Originally Posted by WaynePhillipsEA
How about a slightly different operator to control that behaviour instead of LetSet/Assign?
Code:
MyObject1 := MyObject2
FWIW, I strongly discourage to introduce not "BASICish" syntax like that.
One of the main features and advantage of BASIC is that it can be understood almost "without studing", BASIC is almost English. It is intuitive.
What does ":=" mean? (intuitively)
"I don't know".
-
Aug 18th, 2021, 09:25 AM
#344
Re: TwinBasic
@WaynePhillipsEA,
I'd like to know how twinBasic implements JavaScript-like inline-functions(Callback), for example:
Code:
//Basic forEach
function forEach(array, action) {
for (var i = 0; i < array.length; i++) {
action(array[i])
}
}
// Test forEach
forEach(["Pear", "Apple"], function(name) {
console.log(name);
});
// Implement reduce
function reduce(combine, base, array) {
forEach(array, function(element) {
base = combine(base, element);
});
return base;
}
Another famous example is C++ qsort.
Last edited by SearchingDataOnly; Aug 18th, 2021 at 09:47 AM.
-
Aug 18th, 2021, 10:20 AM
#345
Re: TwinBasic
Originally Posted by Eduardo-
FWIW, I strongly discourage to introduce not "BASICish" syntax like that.
One of the main features and advantage of BASIC is that it can be understood almost "without studing", BASIC is almost English. It is intuitive.
What does ":=" mean? (intuitively)
"I don't know".
Upon reflection, I think you're spot on, and I've rejected other proposed features for those exact reasons. I was purely trying to think of alternatives we hadn't considered, but you are right that this one is not BASIC-esque.
-
Aug 18th, 2021, 10:22 AM
#346
Re: TwinBasic
Originally Posted by Schmidt
Since Niya brought up the "generics-example"...
I've modified it a bit, to (primarily) point out a different "nice to have" feature
(which would be great, if it was introduced).
Code:
Module modHelloRC6
Public New_c As New cConstructor
Sub Main()
Dim Longs As cArrayList = New_c.ArrayList(vbLong, 1, 2, 3)
Dim Persons As cArrayList = New_c.ArrayList(vbObject, New cPerson("John"))
Dim L As Long '<- tB does "For Each Auto-Coercing from Variant" already, also with simple types
For Each L In Longs
Debug.Print L
Next
Dim P As cPerson '<- this would work also in VBA/VB6, whereas the above "L As Long" wouldn't
For Each P In Persons
Debug.Print P.Name
Next
End Sub
End Module
Private Class cPerson
Public Name As String
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
So, whilst the above Code will work "as it is" in tB already (when an RC6-reference is included) -
I'd like to be able to have "LoopVar-TypeInitializers in For- and For-Each Loops, like below:
Code:
Module modHelloRC6
Public New_c As New cConstructor
Sub Main()
Dim Longs As cArrayList = New_c.ArrayList(vbLong, 1, 2, 3)
Dim Persons As cArrayList = New_c.ArrayList(vbObject, New cPerson("John"))
For Each L As Long In Longs
Debug.Print L
Next
For Each P As cPerson In Persons
Debug.Print P.Name
Next
End Sub
End Module
Private Class cPerson
Public Name As String
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
Olaf
I like this idea Olaf, and I can't foresee it causing any clashes with existing syntax, so that's definitely a possibility.
It would also be nice if the declared variable was scoped only to the For block, rather than the procedure-scope.
Last edited by WaynePhillipsEA; Aug 18th, 2021 at 10:29 AM.
-
Aug 18th, 2021, 10:26 AM
#347
Re: TwinBasic
Originally Posted by SearchingDataOnly
@WaynePhillipsEA,
I'd like to know how twinBasic implements JavaScript-like inline-functions(Callback), for example:
Code:
//Basic forEach
function forEach(array, action) {
for (var i = 0; i < array.length; i++) {
action(array[i])
}
}
// Test forEach
forEach(["Pear", "Apple"], function(name) {
console.log(name);
});
// Implement reduce
function reduce(combine, base, array) {
forEach(array, function(element) {
base = combine(base, element);
});
return base;
}
Another famous example is C++ qsort.
Yes, Delegate-style function pointers are planned: https://github.com/WaynePhillipsEA/twinbasic/issues/79
-
Aug 18th, 2021, 11:09 AM
#348
Re: TwinBasic
This single thread is getting quite long already. Shame we can't get our own twinBASIC sub-forum here.
-
Aug 18th, 2021, 01:02 PM
#349
Re: TwinBasic
Originally Posted by WaynePhillipsEA
I like this idea Olaf, and I can't foresee it causing any clashes with existing syntax, so that's definitely a possibility.
It would also be nice if the declared variable was scoped only to the For block, rather than the procedure-scope.
I've thought about that ("For block-scoping") as well,
but would rather the scoping remains at function-level, due to constructs like the one below:
Code:
'the construct here will only be possible, when P As Person would be scoped to Method-level
For Each P As cPerson In Persons
If P.Name = SomeNameToSearch Then Exit For
Next
If P Is Nothing Then 'P is re-initialized to Nothing, when the loop was "running its full course" (no match found)
Debug.Print SomeNameToSearch & " was not found!"
Else 'P is only different from Nothing, when the loop was exited early (via Exit For, because of a match)
Debug.Print SomeNameToSearch & " was found!"
End If
Difficult decision to make (both "scopings" have advantages) - but the stuff shown above is quite common (at least in my code).
Olaf
-
Aug 18th, 2021, 01:08 PM
#350
Re: TwinBasic
Originally Posted by Schmidt
I've thought about that ("For block-scoping") as well,
but would rather the scoping remains at function-level, due to constructs like the one below:
Code:
'the construct here will only be possible, when P As Person would be scoped to Method-level
For Each P As cPerson In Persons
If P.Name = SomeNameToSearch Then Exit For
Next
If P Is Nothing Then 'P is re-initialized to Nothing, when the loop was "running its full course" (no match found)
Debug.Print SomeNameToSearch & " was not found!"
Else 'P is only different from Nothing, when the loop was exited early (via Exit For, because of a match)
Debug.Print SomeNameToSearch & " was found!"
End If
Difficult decision to make (both "scopings" have advantages) - but the stuff shown above is quite common (at least in my code).
Olaf
That's true actually, and will save me from having to make any changes to the scoping
-
Aug 18th, 2021, 02:33 PM
#351
Member
Re: TwinBasic
Originally Posted by Schmidt
I've thought about that ("For block-scoping") as well,
but would rather the scoping remains at function-level, due to constructs like the one below:
Code:
'the construct here will only be possible, when P As Person would be scoped to Method-level
For Each P As cPerson In Persons
If P.Name = SomeNameToSearch Then Exit For
Next
If P Is Nothing Then 'P is re-initialized to Nothing, when the loop was "running its full course" (no match found)
Debug.Print SomeNameToSearch & " was not found!"
Else 'P is only different from Nothing, when the loop was exited early (via Exit For, because of a match)
Debug.Print SomeNameToSearch & " was found!"
End If
Difficult decision to make (both "scopings" have advantages) - but the stuff shown above is quite common (at least in my code).
Olaf
Counter-argument would be that .Net does scope inline declarations like this to the block, and it's occasionally quite nice to be able to reuse a name within the wider scope. For the times when you need the wider scope, you can just declare above the block as before.
I'm ambivilant as to which is best for tB though.
-
Aug 18th, 2021, 02:53 PM
#352
Re: TwinBasic
GUYS I HAVE FINALLY SOLVED IT!!!
Ok, to recap what I'm talking about for anyone just coming in. Yesterday I raised an issue with the Set keyword clashing with generics in TwinBASIC. Writing generic classes that perform assignments internally created situation where a generic class could not work for both classes and non-classes like Longs, Strings etc as the generic type. This was because assignment between class type variables needed to be performed with the Set keyword and assignment between non-class types must be performed without them. There was no way to perform an assignment that works with both types of assignment.
After much discussion in this thread about it, Wayne Phillips pointed out that I could use IsObject to test whether a variable was an object or not and the program can use that information to decide how to perform the assignment. I tried it and it seemed to work in a little scratch pad application I wrote to specifically test it. I thought I had a workable solution. I went into what I was actually using this for which is a List(Of T) and HashTable(Of String,TValue) implementation I'm working on which is going to be 100% written in TwinBASIC code with no external dependencies. When I went back to the List(Of T) code to implement the IsObject solution, it failed. Turns out that for some reason it doesn't work when the assignments involved arrays of type T. Having already spent the better part of the day already on this, I was too burnt out to start this all over again in an attempt to figure out why arrays are now a problem.
The overall problem was that the TwinBASIC compiler is determined not to allow assignments to be perform with the wrong statement, Let for non-objects variable and Set for object variables. If the compile even smells a possibility of this happening, it will complain. I did come up with a way around it by using a bunch on pointer tricks but they are just curiosities. These tricks are only good for showing off and not good for actual use. I went to bed last night defeated. I wrote it off generics as usable until this thing with Set could be sorted out.
But for some reason when I got up today, I decided to give it one more try and I don't know if it was the sleep I needed or what but in 5 minutes, I came up with a way to solve this problem entirely by removing Set from the equation altogether and it didn't involve any fancy tricks with pointers or anything like this. Turns out you can bypass Set completely with a very easy method. Use a function like this:-
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
I'm not even sure why that works as well as it does but it works. That function will allow you to perform an assignment to a variable regardless of if an Object is being assigned or not. Here is the full test program:-
Code:
Module MainModule
' This project type is set to 'Standard EXE' in the Settings file, so you need a Main() subroutine to run when the EXE is started.
Public Sub Main()
Dim objVal As TestClass = New TestClass
Dim strVal As String = "BOO!"
Dim lngVal As Long = 29
Dim objVar As TestClass
Dim strVar As String
Dim lngVar As Long
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
Debug.Print objVar.Value
Debug.Print strVar
Debug.Print lngVar
End Sub
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
End Module
Private Class TestClass
Public Sub new()
End Sub
Public Property Get Value() As String
Return "Class Property Value"
End Property
End Class
I popped that into the List(Of T) class I'm writing and it works. The List(Of T) class can now work with both class and non-class types with no problem. And it has no problem working with arrays.
-
Aug 18th, 2021, 03:36 PM
#353
Re: TwinBasic
Originally Posted by Niya
GUYS I HAVE FINALLY SOLVED IT!!!
Ok, to recap what I'm talking about for anyone just coming in. Yesterday I raised an issue with the Set keyword clashing with generics in TwinBASIC. Writing generic classes that perform assignments internally created situation where a generic class could not work for both classes and non-classes like Longs, Strings etc as the generic type. This was because assignment between class type variables needed to be performed with the Set keyword and assignment between non-class types must be performed without them. There was no way to perform an assignment that works with both types of assignment.
After much discussion in this thread about it, Wayne Phillips pointed out that I could use IsObject to test whether a variable was an object or not and the program can use that information to decide how to perform the assignment. I tried it and it seemed to work in a little scratch pad application I wrote to specifically test it. I thought I had a workable solution. I went into what I was actually using this for which is a List(Of T) and HashTable(Of String,TValue) implementation I'm working on which is going to be 100% written in TwinBASIC code with no external dependencies. When I went back to the List(Of T) code to implement the IsObject solution, it failed. Turns out that for some reason it doesn't work when the assignments involved arrays of type T. Having already spent the better part of the day already on this, I was too burnt out to start this all over again in an attempt to figure out why arrays are now a problem.
The overall problem was that the TwinBASIC compiler is determined not to allow assignments to be perform with the wrong statement, Let for non-objects variable and Set for object variables. If the compile even smells a possibility of this happening, it will complain. I did come up with a way around it by using a bunch on pointer tricks but they are just curiosities. These tricks are only good for showing off and not good for actual use. I went to bed last night defeated. I wrote it off generics as usable until this thing with Set could be sorted out.
But for some reason when I got up today, I decided to give it one more try and I don't know if it was the sleep I needed or what but in 5 minutes, I came up with a way to solve this problem entirely by removing Set from the equation altogether and it didn't involve any fancy tricks with pointers or anything like this. Turns out you can bypass Set completely with a very easy method. Use a function like this:-
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
I'm not even sure why that works as well as it does but it works. That function will allow you to perform an assignment to a variable regardless of if an Object is being assigned or not. Here is the full test program:-
Code:
Module MainModule
' This project type is set to 'Standard EXE' in the Settings file, so you need a Main() subroutine to run when the EXE is started.
Public Sub Main()
Dim objVal As TestClass = New TestClass
Dim strVal As String = "BOO!"
Dim lngVal As Long = 29
Dim objVar As TestClass
Dim strVar As String
Dim lngVar As Long
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
Debug.Print objVar.Value
Debug.Print strVar
Debug.Print lngVar
End Sub
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
End Module
Private Class TestClass
Public Sub new()
End Sub
Public Property Get Value() As String
Return "Class Property Value"
End Property
End Class
I popped that into the List(Of T) class I'm writing and it works. The List(Of T) class can now work with both class and non-class types with no problem. And it has no problem working with arrays.
Don't get too excited... that looks like a bug in tB, as it should be trying to call the default prop-let member in that assignment inside SetVarValue if the LHS is an object type. That will be fixed very soon, and then your code will break.
You could put the IsObject check into the SetVarValue call to switch between the two types of assignments, and that should satisfy the compiler and be correct. Though of course you are passing the values through Variants for that to work, which as @wqweto mentioned earlier, goes against what you're trying to achieve by using generics.
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal Value As Variant)
If IsObject(varToSet) Then
Set varToSet = Value
Else
varToSet = Value
End If
End Sub
Last edited by WaynePhillipsEA; Aug 18th, 2021 at 03:44 PM.
-
Aug 18th, 2021, 03:53 PM
#354
Re: TwinBasic
BTW, this code:
Code:
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
is also not allowed in VB6 (arguments in parentheses without the Call keyword).
-
Aug 18th, 2021, 04:01 PM
#355
Re: TwinBasic
Originally Posted by Eduardo-
BTW, this code:
Code:
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
is also not allowed in VB6 (arguments in parentheses without the Call keyword).
It is allowed in tB though, whilst retaining full backwards compatibility. See the discussion:
https://github.com/WaynePhillipsEA/twinbasic/issues/262
-
Aug 18th, 2021, 04:20 PM
#356
Re: TwinBasic
Originally Posted by WaynePhillipsEA
This single thread is getting quite long already. Shame we can't get our own twinBASIC sub-forum here.
It is fine to create multiple threads in Other Basic, then if/when a twinBASIC forum is created I (or other moderators) will move the threads across.
-
Aug 18th, 2021, 04:24 PM
#357
Re: TwinBasic
Originally Posted by WaynePhillipsEA
Ah, OK, then could we say that now the Call keyword is entirely optional/obsolete?
-
Aug 18th, 2021, 04:34 PM
#358
Re: TwinBasic
Originally Posted by si_the_geek
It is fine to create multiple threads in Other Basic, then if/when a twinBASIC forum is created I (or other moderators) will move the threads across.
Good to know, thanks!
-
Aug 18th, 2021, 04:43 PM
#359
Re: TwinBasic
Originally Posted by Eduardo-
Ah, OK, then could we say that now the Call keyword is entirely optional/obsolete?
I would say mostly optional/obsolete. There will be edge cases, e.g. if a procedure is named the same as a syntax keyword, then the call prefix can still help to disambiguate between the keyword and the procedure name. But in the majority of cases it should be considered obsolete/optional.
-
Aug 18th, 2021, 04:45 PM
#360
Re: TwinBasic
Originally Posted by WaynePhillipsEA
Don't get too excited... that looks like a bug in tB, as it should be trying to call the default prop-let member in that assignment inside SetVarValue if the LHS is an object type. That will be fixed very soon, and then your code will break.
You could put the IsObject check into the SetVarValue call to switch between the two types of assignments, and that should satisfy the compiler and be correct.
I would be totally fine with that IsObject logic as long as it prevents the compiler from interfering with what I'm trying to do. The real value here is being able to use Variants on both sides so that the compiler doesn't try to tell me what I can and cannot do. With Variants involved on both sides of the assignment, the compiler cannot make any assumptions, it has to be evaluated at runtime for correctness, which is exactly what I want. The other big win here is that I don't have to refactor my class code, all I have to do is use the function where I'm performing assignments that involve type T. That's a very minor cost to pay in my opinion.
Originally Posted by WaynePhillipsEA
Though of course you are passing the values through Variants for that to work, which as @wqweto mentioned earlier, goes against what you're trying to achieve by using generics.
This is true. However, I'm willing to live with that. Generics provide more value than just performance. I'd argue that the bigger pay off is type safety. I'm willing to sacrifice a bit of performance for the type safety that generics give you. If I have a list of Longs, I only want Longs in that list and I would like the compiler to stop me if I ever make this mistake of trying to put in a String. This is a big win for me that helps prevent all kinds of errors in code. If I have to lose a few clock cycles for it, so be it.
This is the sole reason I hate JavaScript with its duck typing nonsense. It spoils an otherwise very beautiful language. It puts too much of a burden on you to always make sure you're using the right data types. There is a lot of value in a compiler that has ways of easing this burden so I can put my mental more towards what I'm actually doing rather than how I'm doing it. This is just my opinion though, there is no right or wrong here. It's just preferences.
Originally Posted by Eduardo-
BTW, this code:
Code:
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
is also not allowed in VB6 (arguments in parentheses without the Call keyword).
Oh boy. This is another big discussion. I really don't want to go down that path right now. I'll just say this, like Set, this is another one of those garbage things present in the VB6 language that makes zero sense and adds no value. No programming language I know of would allow something like that. I'm a huge fan of consistency in a programming language, and a function/sub call should always be in brackets. I'm only speculating here but I suspect that VB6 inherited that nonsense from QuickBasic to support Print and statements like it so you can do stuff like this:-
Code:
Debug.Print "This is ";
Debug.Print "one line"
Requiring brackets there would make the syntax look too cluttered. Of course I'm just speculating here. I really don't know why BASIC was the only mainstream language that allowed this. Suffice to say, it's confusing as hell when you start switching between writing code in BASIC and some other language such as C. Sometimes you bring back the habit of bracketing your function calls to BASIC and it can become annoying when the compiler stops you and demand that you fix it. I've experienced it and still do to this day when switching between VB6 and VB.Net which is consistent about function call syntax.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|