dotnet / vblang

The home for design of the Visual Basic .NET programming language and runtime library.
290 stars 63 forks source link

Units Of Measure #200

Open AdamSpeight2008 opened 6 years ago

AdamSpeight2008 commented 6 years ago

I propose that we borrow from F# the language feature of Units Of Measure.

Units Of Measure expands on the type-safety principles, by optionally enhancing the type with a explicit kind.


Basic Structure (draft)

UnitOfMeasureBlock  ::= Unit_Header Unit_Body Unit_Footer
Unit_Header         ::= "Unit" Unit_Identifier Unit_Constraint?
Unit_Constraint     ::= "As" typeidentifier
Unit_Footer         ::= "End" "Unit"
Unit_Body           ::= Unit_BodyLine*
Unit_BodyLine       ::=
Unit_Definition     ::= "[" Unit_Identifier_Symbol "]"
Unit_Operator       ::=
Unit_CompositeBlock ::= 

Imports UnitOfMeasure.SI

Module Example

  Public Sub Main()
    Dim  G = 9.98[m/s^2]
    Dim speed As [m/s] = distance[m] / time[s]
    Dim Area As [m^2] = height[m] * width[m]
    Dim Volume As [m^3] = height[m] * width[m] * depth[m]
  End Sub

End Module

Define a Unit Of Measure

Implemented by a new block construct Unit .... End Unit, which is, similar to a namespace block. These blocks can be imported.

The language will provide a base set of units of measure (SI Units and their derived Units)

Abstract Unit SI
  Abstract Unit Distance
    [m]  ' metres  ( A concrete implementation of an abstract unit SI.Distance
    [m^2] = [m] * [m]
    [m^3] = [m] * [m] * [m]
    [m^3] = [m^2] * [m]
    [m^3] = [m] * [m^2]
  End Unit

  Abstract Unit Mass
    [kg] ' kilograms (mass)
  End Unit

  Abstract Unit Duration
    [s]  ' seconds
  End Unit

  Abstract Unit Temperature
    [K]   ' Kelvins
    [K].Min = 0[K] ' Add constraint on the valid range of this Concrete Unit
  End Unit

  Abstract Unit AmountOfSubstance
    [mol] ' Mole
  End Unit  

  Abstract Unit LuminousIntensity
    [cd]  ' candela
  End Unit

  Unit ElectricCurrent
    [A]   ' Amperes
  End Unit

End Unit
Unit SI_Derived

  Unit Distance
    Constant OneMinute = 1[min]
    Constant SixtySeconds = 60[s]
    ' Explicit Conversions
    [min] = [s] / 60
    [s]   = [min] * 60
  End Unit

  Unit Duration
    ' Explicit conversions
    [ms] = [s] / 100
    [ns] = [s] * 1000000
  End Unit

  Unit Frequency
    [Hz] ' Hertz
    ' Permitted operators 
    [Hz] = [1/s]   ' Operator /( C As Constant, s As [s] ) As [Hz]
    [Hz] = [s^-1]  ' Operator ^( s As [s], C as Constant ) As [Hz]
    ' Define conversion factors.
    [KHz] = [Hz] / 1000
    [MHz] = [KHz] / 1000
    [GHz] = [MHz] / 1000
    [THz] = [GHz] / 1000
  End Unit

End Unit
Const C = 299792458[m/s]
Const G = 9.98[m/s^2]
Const Avogadro= (6.022140857 x 10^23)[mol^-1]

Composite Units Of Measure

If an Unit Of Measure is followed by another Unit Of Measure, that is compos-able with it. It combines into a single composite UnitOfMeasure Example

Unit Feet
  [ft]
  [']  ' an alias for [fr]
  Composite ["]( x As Double["] ) As [Ft][In] 
  End Composite
End Unit

Unit Inch
  [in]
  ["]  ' an alias for [in]
End Unit

```vbnet
Dim height = 6[']6["] 'eg 6 foot and 6 inches.
Dim length = 6["]6['] ' Invalid: Inches can't compose with feet. (Did you mean a Feet and Inches?)
reduckted commented 6 years ago

120

AdamSpeight2008 commented 6 years ago

Alternative Syntax

Unit SI
  .Distance
    [m]  ' metres    (length)
  .Mass
    [kg] ' kilograms (mass)
  .Duration
    [s]  ' seconds
  .Temperature
    [K]  ' Kelvins
    [K].MinValue = 0
  .AmountOfSubstance
    [mol] ' Mole
  .LuminousIntensity
    [cd]  ' candela
  .ElectricCurrent
    [A]   ' Amperes
End Unit

Unit SI_Derived
  .Distance
    [min] = [s] / 60
    [s]   = [min] * 60
  .Duration
    [ms] = [s] / 100
    [ns] = [s] * 1000000
  .Frequency
    [Hz] ' Hertz
    [Hz] = [1/s] 
    [Hz] = [s^-1]
    [KHz] = [Hz] / 1000
    [MHz] = [KHz] / 1000
    [GHz] = [MHz] / 1000
    [THz] = [GHz] / 1000
  .Angle
    [deg] ' Degrees
End Unit
Bill-McC commented 6 years ago

Is this different from #120 ?

AdamSpeight2008 commented 6 years ago

@Bill-McC it is a different style to #120.

gilfusion commented 6 years ago

Do units of measure even need to be defined? Could they be handled the same way as named tuples today? We could define the units in place, either in member/local declarations or in the literals, like names in tuples. Conversions could be handled by regular old functions. (Or do you think implicit conversions are an essential part of this feature?)

Implementation-wise, I can imagine something like this

Function CalcVelocity(distance As Double[m], time As Double[s]) As Double[m/s]

becoming something like this

Function CalcVelocity(<Unit("m")> distance As Double, <Unit("s")> time As Double) As <Unit("m"), InvertedUnit("s")> Double

behind the scenes.

AdamSpeight2008 commented 6 years ago

@gilfusion I think units of measure should be defined, as the operators that be are specific to that unit. They maybe able to be implemented via tuple (I've not look at implementation), but I rather them be an intrinsic part of the language, and also framework.

In general the only universal ones are + (addition) and `-' (subraction) within the same unit of measure. Any other should be defined upon the specific units. Which the advantages of using units of measure, come into play, it catches issues and errors at compile-time.

Dim x = 10[m]
Dim y =   1
Dim q = x + y ' Compile-Time Error: Unit [m] doesn't support operator + with an integer. 

Dim  length = 10[m]
DIm quantity = 10
Dim total_length = length * quantity. ' Valid (if Unit[m] defines *( L As Double[m], R As Double ) As Double[m]

All units of measure should be Explicit then any conversion from unit to another to is explicitly made, so the user is sure about the potential loss of precision. An example using Composite Units Of Measure converting Imperial units to Metric unit (or visa-versa) would require an explicit cast, Note I'm also using a new cast operator CUoM eg CastUnitofMeasure)

Dim imperial_height = 6[']6["] ' eg 6 foot and 6 inches.
Dim metric_height As Double[m] = imperial_height ' would be an error
AdamSpeight2008 commented 6 years ago

Thought of another usage of Composite Unit, indication of precision

Dim duration = measured_time[s][+-]25[ms]
' measured time period with an accuracy of plus or minus 25 milliseconds.
gilfusion commented 6 years ago

@AdamSpeight2008 Okay. I think we are thinking of two different approaches to units of measure. I was just thinking of them as an annotation, like an attribute, to mark a numeric value, but I think you are thinking of them as a new kind of data type (kind of like how Enums are their own data types). Does that sound right?

Also, looking at that precision example, do you see a way to get the individual pieces of a composite unit out? To get the inches portion out of a feet-inches composite, or get the precision part out of a time-precision composite?

AdamSpeight2008 commented 6 years ago

@gilfusion Sounds right, I'm treating them as a new kind of datatype.

Also, looking at that precision example, do you see a way to get the individual pieces of a composite unit out? To get the inches portion out of a feet-inches composite, or get the precision part out of a time-precision composite?

Note This is extremely rough back of envelope code Compiler could a set of abstract class and concrete classes, to which the Unit Of Measure code compile to. There will also require additional compilation step. Compile Units -> Compile Code eg

MustInherit Class Unit
  Public ReadOnly Property Symbol As String
  Public ReadOnly Value As ??

  Friend Sub New( Symobol As String, Value As ??)
    Me.Symbol = Symbol
    Me.Value  = Value
  End Sub

  Overrides Function ToString() As String
    Return $"{Value.ToString}[{Symbol}]"
  End Function

End Class

Class WithPrecision
  Public ReadOnly Property Source As U0

  Sub New( source As Unit,  value As ?? )
    MyBase.New("+-", value)
    Me.Source = Source
  End Sub

  Overrides Function ToString() As String
    Return $"{Source.ToString}[MyBase.ToString]"
  End Function

End Class

Class Second : Inherits Unit.Time
  Sub New( Value As ??)
    MyBase.New("s", Value )
  End Sub
End Sub
AdamSpeight2008 commented 6 years ago

We could have a partial solution to Complex Number Literals

Dim x As Numerics.Complex = -12.34[i]56.78
AdamSpeight2008 commented 6 years ago

I've a repo that is developing the base class and initial Units. https://github.com/AdamSpeight2008/Unit @AnthonyDGreen VBnet Units Defined