@jotter I think you also miss the use of VARIANT as wholly unhelpful
A person could pass in an integer - and the method call would compile and fail spectacularly at run time
Pushing errors out to runtime is probably one of the worst things about this design
The earlier you catch errors the better
Catch them at design time they are easy to fix - no code written yet
Change the design
At compile time - well not perfect but no code has shipped
But at runtime its way too late !
I’m sure I hear the
Oh but just TEST the type at runtime to make sure its a Graphics or WebGraphics !
So now the code is
#If TargetDesktop Or TargetMobile Then
if (context isa graphics) = false then
return
end if
Var g As Graphics = context
#EndIf
#If TargetWeb Then
if (context isa WebGraphics) = false then
return
end if
Var g As WebGraphics = context
#EndIf
That still leaves the issue as a RUNTIME one
and the CALLER is 100% unaware that their call has failed when passing the wrong parameter type (ICK !)
The compiler cant help you at all (which I personally think is a huge downfall)
An interface would certainly make life a bit easier - at least the graphics & web graphics would present a similar API
Variants subvert the type subsystem in Xojo and should be avoided so the compiler CAN help warn about wrong parameter types, type mismatches etc
Having to resort to them for this code is great example of a bad design leaking into the wild where you have no choice but to deal with that bad choice
Something you MIGHT consider is writing your own wrapper class
So DrawBoard looks like
DrawBoard(context as MyWrapper)
And calls to it are like
Var cb As New Checkerboard
dim wrapper as new MyWrapper(g)
cb.DrawBoard( wrapper(g) )
cb.DrawCheckers(wrapper(g) )
MyWrapper then can have one constructor for Web and one for Desktop (thanks to compatibility flags)
the web only constructor
Public Sub Constructor(g as webGraphics)
instance = g
End Sub
the desktop only constructor
Public Constructor(g as Graphics)
instance = g
end
Instance might be a variant property internally (not exposed to the world)
And then the MyWrapper exposes the various drawing methods of whatever it got called with
Sub DrawingColor(assigns c as Color)
#If TargetDesktop Or TargetMobile Then
Var g As Graphics = context
#EndIf
#If TargetWeb Then
Var g As WebGraphics = context
#EndIf
g.drawingColor = c
end sub
function DrawingColor() as color
#If TargetDesktop Or TargetMobile Then
Var g As Graphics = context
#EndIf
#If TargetWeb Then
Var g As WebGraphics = context
#EndIf
return g.DrawingColor
end function
and so on
At least this way YOU can provide some consistency by wrapping it however you want
And the compiler can help you out by flagging erroneous calls and usage
For their Example Project it would be enough to just use the Constructors approach (without the overhead of the Wrapper class - you know… they’re marketing for “citizen developer”, not more experienced ones ).
Class 'Checkerboard':
Public Sub Constructor(g As WebGraphics) // Include in Web only, Public
context = g
End Sub
Public Sub Constructor(g As Graphics) // Include in Desktop and Mobile, Public
context = g
End Sub
Private Sub Constructor() // Private, so you can't instantiate without Parameter
End Sub
And I’d also change the conditional compilation parts… What if at some time it’s neither TargetDesktop, TargetMobile and also not TargetWeb? Not good enough to just check with #If for a single case.
#If TargetDesktop Or TargetMobile Then
Var g As Graphics = context
#ElseIf TargetWeb Then
Var g As WebGraphics = context
#Else
#Pragma Error "Unsupported Target"
#EndIf
Oh well… I guess we all have ideas and suggestions how to do better.
All I wanted to point out on TOF is that providing such example projects (and even marketing them via Blog…) doesn’t look good for them
And here I thought you were originally going to point out the inanity of having two differently named objects for the same function in a “X Platform” environment…
Also not exactly related to the original post’s point (which is well taken), but this is where I really have come to appreciate Julia’smultiple dispatch. I’d also note that plotting/graphics libraries these days present a common API but allow you to swap out backends. Seems like maybe that’s what should be happening here if the difference is drawing to a desktop graphics context vs something like a web canvas.
Jurg already did that in his original post on TOF
And the I agree - but then they did the whole rename Buttons as DesktopButton, WebButton, iosButton, MobileButton thereby ruining any shared code possibilities there on much the same basis
Generic code has to use variants and type check at runt time to make sure the code doesnt blow up because you passed an INTEGER not a button
Strangely, I seem to spend about zero time on TOF anymore…
These kinds of workarounds (again, due to an unforced error in the first place), and all the rest of the gymnastics with interfaces, etc. (hey, wasn’t inheritance supposed to manage all of that?) has soured me on OOP. Not just Xojo, but I’ve seen similar contortionism in C++ and Python. Julia’s multiple dispatch handles things elegantly, giving you what you mean, and seems eminently more approachable (and certainly more explainable) to “citizen developers.”
No - Xojo is like Java here and only supports single inheritance so interfaces are extremely common (multiple inheritance has its own set of problems - looking at you C++)
C++ has bigger issues since it does support multiple inheritance
Leaving things until runtime defers the problems, if any, until later in the cycle and that becomes more costly to fix.
Dunno if the compiler could alert a person to an ambiguous usage but I know dynamic dispatch based on runtime type has been requested before
Right now it uses declared type which has its own set of issues
Julia is AOT/JIT compiled, with types inferred or annotated. In some sense, the compiler is there at runtime. There are also tools for helping to achieve what’s referred to a type stability.
Since it isn’t OOP, we’re not dispatching on the (singular) object type, but rather the runtime types of method arguments as a whole.
Which could be considered insulting to the intellect of Xojo’s customers because it’s the opposite direction of most x-platform toolkits.
For example, in SwiftUI which covers Mac, iPad, iPhone, Android*, Watch, TV and Vision Pro, it’s the same code to make a Button, it’s even just called Button.
*to compile for Android requires a 3rd party tool.
But Apple only properly supports it’s own ecosystem, so Swift and SwiftUI are not really x-platform tools. I think, 3rd party Android transpiler is also a bit pricey at 99$/month.
But also at codenameone it is one button for Android, iOS, Web and also Desktop. There is no difference and there has no difference to be included. XoJo could implement exactly that also without any problem. It is even for free. Also you could implement with Kotlin Multiplatform or you implement with JavaFX and GluonMobile/Desktop. Many possible ways to do and I think even C# has capabilities to do so. Xojo is not one of them. They have implemented their own ecosystem. Source compatibility is not one of their targets.
Geoff Perlman describes the role of citizen developers in Xojo's ecosystem as central and empowering, emphasizing that Xojo is designed to enable individuals who may not be professional developers to create powerful, native applications.
yet many many times he has said the exact opposite, therefore fitting the narrative to the situation at hand