fltkhs icon indicating copy to clipboard operation
fltkhs copied to clipboard

What is the purpose of duplicate functions?

Open mitchellwrosen opened this issue 6 years ago • 12 comments
trafficstars

For example, the Button module defines

  • handleButtonBase
  • resizeButtonBase
  • hideButtonBase
  • showWidgetButtonBase

but there also exists

  • impl ~ (Event -> IO (Either UnknownEvent ())) => Op (Handle ()) ButtonBase orig impl
  • etc.

with (as far as I can tell) the same types. Is there a difference between these? Thanks!

mitchellwrosen avatar Apr 23 '19 02:04 mitchellwrosen

It's so one can call base class methods. While the type signatures are the same the underlying method call is different.

deech avatar Apr 23 '19 03:04 deech

I see... I'm still having some difficulty understanding. Which call is which? I'm not entirely clear on what BaseButton is, and the source you linked also mentions DerivedButton. However, neither of these are defined in FLTK proper, at least according to this diagram.

mitchellwrosen avatar Apr 23 '19 03:04 mitchellwrosen

I read over the docs a bit more and I'm starting to understand. My understanding now is that in fltkhs, FooBase is Fl_Foo. The non-Base versions have no analogous class in C++. A Ref Foo, then, is exactly the same thing as a Ref FooBase (which is to say, has exactly the same API as, and whose functions behave identically to), except for the things you mentioned in #112.

mitchellwrosen avatar Apr 23 '19 15:04 mitchellwrosen

Yes exactly and I admit this is confusing. Basically the inheritance chain looks like:

ABase -> A
 |
 v
BBase -> B
 |
 V
CBase -> C
 |
 v

where A,B, and C are the widgets you construct and use. Notice it is impossible use the *new functions to create any Base widgets. The reason for this confusing architecture is that every widget that you use on the Haskell side has a custom destructor that cleans up callbacks into the Haskell runtime when it's deleted so the GHC GC can free the resources they close over. Because of that each user constructable widget is essentially a branch point the inheritance chain.

deech avatar Apr 23 '19 16:04 deech

Interesting! A follow-up question, if I were to write my own Button, would I subclass like this?

WidgetBase -> Widget
 |
 v
ButtonBase -> Button
           -> MyButton

Or like this?

WidgetBase -> Widget
 |
 v
ButtonBase -> Button -> MyButton

If the former, is there any functionality in Button that I'm "leaving behind"...? (And presumably would want to re-implement in MyButton)

mitchellwrosen avatar Apr 23 '19 16:04 mitchellwrosen

If you want to create your own button you'd use buttonCustom which allows you to customize a small set of essential functions. Overriding arbitrary functions in the inheritance chain is not supported.

deech avatar Apr 23 '19 16:04 deech

Got it. Another question (sorry!) - the *Base types do crop up in the API, for example in

getParent :: Ref WidgetBase -> IO (Maybe (Ref GroupBase))

you're going to get back a Ref GroupBase no matter what type you started with. Is there a reason why this isn't Ref Group - going off of an assumption that the *Base types are somewhat "internal"?

mitchellwrosen avatar Apr 23 '19 16:04 mitchellwrosen

Also while this is limiting you can do quite a bit with just those. The entire fltkhs-themes package relies mostly custon draw and handle to get a completely different look-and-feel.

deech avatar Apr 23 '19 16:04 deech

Ah yes, they do crop up in return types, you're right. You don't want a Ref Group there because it is only guaranteed to be a GroupBase (ie. Fl_Group) since the parent may not have been constructed by you but by methods on the C++ side. This is also why getChild returns Ref WidgetBase instead of widget.

deech avatar Apr 23 '19 16:04 deech

Very helpful! Could you clarify why one might not want to immediately upcast every Foo they encounter to a BaseFoo? I'm still struggling to understand the essence of the distinction (from an API standpoint).

mitchellwrosen avatar Apr 23 '19 16:04 mitchellwrosen

Oh - is the answer that in doing so, you'd toss out your custom overrides?

mitchellwrosen avatar Apr 23 '19 17:04 mitchellwrosen

Hmm.. let me think about that. You maybe right, you can't really add member functions to a child when you override so a Foo could be used safely as BaseFoo but you shouldn't need to upcast like that for normal cases. Even when passing some MyButton into another function I have a Parent constraint that does the check for you.

deech avatar Apr 23 '19 17:04 deech