Pseudo-Inheritance

Topics: C# Language Design, General, VB Language Design
Aug 25, 2014 at 1:22 PM
Edited Aug 25, 2014 at 1:31 PM

Pseudo-Inheritance

Let's inherit from a structure or maybe sealed class, especially useful for simple types to ensure type-safety of values.
eg
struct Miles <: Double { }
struct Kgs   <: Double { overrides string ToString() { return $"{base}Kgs" } }
struct Metres <: Double {  overrides string ToString() { return $"{base}m " } }
These have the same basic structure as a double only addition. subtraction and "comparison" operators are implemented by default. Is implicitly cast able to double. explicit cast required to go other way.
var distance = (metres)100.00
var time  = (seconds) 9.86
var speed = new per<Metres,Seconds>(distance, time)
by extending the pseudo-inheritance another level, you could implement the basis of units of measure.
base struct Distance <: Double
{ static operator +<T : Distance>(T d0, T d1) : T { /* */} }
base struct Time <: Double
base struct Mass <: Double

struct Metres <: Distance
struct Seconds <: Time
struct Kgs <: Mass
Aug 26, 2014 at 8:52 PM
Consider the following case:
1) Assembly Foo.dll version 1.0.0 has type X
2) Assebly Bar.dll 1.0.0.0 has type Y that is inherited from type X
3) User of Bar.dll (downloaded from NuGet) wants to use Foo.dll version 2.0.0.0, downloads it from NuGet and adds it to binding redirects.

With normal inheritance all changes in type X will take place, with pseudo-inheritance that behaviour will be broken. However, constants and default argument values are currently being hardcoded in the assembly that uses them, so it might be not an issue.
Aug 27, 2014 at 9:31 PM
kekekeks wrote:
With normal inheritance all changes in type X will take place, with pseudo-inheritance that behaviour will be broken. However, constants and default argument values are currently being hardcoded in the assembly that uses them, so it might be not an issue.
I think a lot would depend upon whether pseudo-inheritance is a compile-time or run-time concept. The only way I can see in which a compiler could safely allow pseudo-inheritance of a sealed type would be to have all uses of that type in source code converted to the "real" type in the CIL. For example, given a type KelvinTemperature which pseudo-derives from System.Double, a statement var myArray = new KelvinTemperature[12]; would create a 12-element array of Double. A compiler could allow extension methods to be defined which would work on KelvinTemperature but not Double, so the types wouldn't be synonymous to the compiler, but the generated code would make no distinction. Casting a KelvinTemperature to Object would yield a boxed Double, and casting any boxed Double to KelvinTemperature would simply import the value without regard for whether the value was held in a KelvinTemperature before boxing.

If pseudo-derived types were implemented in that fashion, I don't think versioning would be a particular problem, since storage locations and instances of the pseudo-derived type would actually be things of the base type.
Aug 27, 2014 at 10:05 PM
They would be distinct type at both compiler and runtime.
Metre is just reusing some to internal of double.

Look at the code you have to write, and that not all of it.
Class Metres
  Public ReadOnly Propery Value As Double
  
  Private Sub New( Value As Double)
    _Value = Value
  End Sub

  Shared Public Operator +( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value + y.Value)
  End Sub

  Shared Public Operator -( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value - y.Value)
  End Sub
  Shared Public Operator +( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value + y.Value)
  End Sub
  Shared Public Operator =( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value = y.Value)
  End Sub
  Shared Public Operator <>( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value <> y.Value)
  End Sub
  Shared Public Operator <=( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value <= y.Value)
  End Sub
  Shared Public Operator >( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value > y.Value)
  End Sub
  Shared Public Operator >=( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value >= y.Value)
  End Sub
  Shared Public Operator <( x As Metres, y As Metres) As Metres
    Return New Metres(x.Value < y.Value)
  End Sub
If you could just write
Struct Metres <: Double
End Struct
A wham the compiler synthesis some of the basic core methods for you.

Like Duck Typing excepting it is what I'll say name as Goose Typing. Looks similar to a duck but isn't a duck.

Cast a Metre to a Object and its a boxed Metre
Storage would be the same as Double. It just has a different typename.
Aug 28, 2014 at 6:15 PM
Edited Aug 28, 2014 at 6:15 PM
If Metres inherits operators from double, then the return type of an operator like + would be double, not Metres. Merely saying that Metres should behave like double except with all references to type double replaced with Metres would leave ambiguous the question of what should happen with operations involving mixed types. Bear in mind that the product of a Metres and a double should be a Metres, while the product of two Metres should be a SquareMetres.
Aug 28, 2014 at 8:21 PM
Don't confuse Metres with being a Double, it isn't.

What does the double 1.23 represent. ?
For example:- If I give you the task of tell be with an high degree of certainty what some random block (of random length) of memory represents. You could guess at a what it represents, but it's still a guess.
On it's own it means nothing it's just a value.
Where as if I said the it was some particular encoding, you'll have a better chance of know what it represents

Now if specific a measurement system. (Metres)1.23. That 1.23 as some contextual content:- That of it represents metres.
Yep what is 1,23 metres is a different context

Metres only inherits basic operators like Addition and Subtraction. Other operators requires the programmer to explicitly to implement.
 Metres + Double  ' Not allowed 
 Double + Metres  ' Not allowed 
 Double + Double -> Double
 Metres + Metres -> Double

 /* Casting */

 implicit Metres -> Double /* Metres ISA "type" of double pseudo-inherit  */
 explicit Double -> Metres
  /* A double isn't necessarily represents Metres, could be Second or Inches.
     So the it is the responsibility of programmer.   
  */
What I'm proposing is getting the IDE/Compiler have write some of the required boiler-code, for you.
Wrapping a Double inside another type.
public struct Metres
  Public ReadOnly Property Value() As Double  ' <- Why?
Why not
public struct Metre
  Inherits Double

  ' Me 
End struct
It could reduce some to the code

Shared Public Operator CType ( X As Metres ) As Double
  Return X.Value ' ??
End Operator

Shared Public Operator Explicit CType ( X As Double ) As Metres
  Return New Metres( X )  ' <<<
  '
End Operator
It is if double can have default indexer that doesn't require parameters. If Double can do that, why can't my types? Especially if it just a wrapping a "core" type.
Shared Public Operator +( X As Metres , Y As Metres ) As Metres
  Return CType(CType( X ,Double) + CType( Y ,  Double)), Metres)
End Operator
or
Shared Operator +( X As Metres, Y As Metres ) As Metres
  Return New Metres( X.Value + Y.Value )
End Operator
Aug 28, 2014 at 9:46 PM
Is addition on double atomic?
Aug 28, 2014 at 9:50 PM
AdamSpeight2008 wrote:
Is addition on double atomic?
Short answer, no.