Closed matthewcrews closed 4 years ago
This would be really cool. Looking forward to it.
Yeah, I took an initial stab at it and saw that it was going to be a challenging lift. I have some other projects that need to be completed first before I can come back to this.
I have updated the initial comment to have much more detail about how this would ideally behave.
After mulling this over, I believe it will require creating new types Scalar<'Measure>
, Decision<'Measure>
, and LinearExpression<'Measure>
in a separate module that users will need to open. The nice thing is that this would not break any existing code. The downside is that it create duplicate, boilerplate code.
I believe the new Module would be Flips.Domain.UOM
. My biggest concern is that it would cause ambiguity and confusion for people who use the library. Opening Flips.Domain
and Flips.Domain.UOM
in the wrong order could cause confusion.
After mulling this over, I believe it will require creating new types
Scalar<'Measure>
,Decision<'Measure>
, andLinearExpression<'Measure>
in a separate module that users will need to open. The nice thing is that this would not break any existing code. The downside is that it create duplicate, boilerplate code.I believe the new Module would be
Flips.Domain.UOM
. My biggest concern is that it would cause ambiguity and confusion for people who use the library. OpeningFlips.Domain
andFlips.Domain.UOM
in the wrong order could cause confusion.
š Sound a feasible/great design, except .... Flips.Domain.UOM doesn't appear to be ..... a nice name IMHO ....
@wxinix I'm open to suggestions :)
Flips.Domain.UnitsOfMeasure
?
I am sure other libraries have done something like this, I just have not seen them. If there is an established precedent in the F# community, I would love to hear about it.
As I mull, it is much simpler to update the existing types with UoM support. The downside is that the types will be forced to have a UoM information. For example:
// This
let x = Decision.createContinuous "Name" 0.0 infinity
> x // Decision
// Will now have the following type
> x // Decision<1>
Where <1>
is the unit-less UoM in F#. I could see this additional notation being possibly confusing.
Scalar
and Scalar<'M>
, Decision
and Decision<'M>
, LinearExpression
and LinearExpression<'M>
Probably a separate module is a better (clearer) design that doesn't mix tastes. Using separate modules provide two tastes, which is good.
Probably a separate module is a better (clearer) design that doesn't mix tastes. Using separate modules provide two tastes, which is good.
Agreed. I've been poking around with some proof of concept stuff to see what could be done. The hard part will be to keep things cleanly separated but also easy to move back and forth when necessary. Clarity for the user is a high priority. UoM should make it easier to use the library, not harder.
So, I think I have a first pass of this done and an open PR
I had to move the types around to avoid name conflicts. I don't think it will cause too much of an issue but I could not find a way around it.
I need to work some examples with the Units of Measure to make sure the ergonomics behave the way I want. Right now UOM is all-in or all out. You can't really mix them. This was purposeful because I thought that mixing them would cause horrible confusion. Once I put together some examples I am sure there will be some edge cases that need to be smoothed over.
The PR is out there if anyone wants to look at it or comment.
So I got this working and the ergonomics are good. I need to add some functions for getting the results values in the original UoM but this is looking pretty close. This will likely be a 2.0.0 release since I had to move some stuff around and this is a significant increase in functionality (at least for me š). The complexity of using the library hasn't increased, things were just not laid out as well as they should have been initially.
I consider keeping UoM code distinct from the non-UoM code to prevent a user from accidentally mixing them and causing horrible compiler errors that do not make sense. UoM is meant to be all or nothing.
I hope to release this by early next week. I am also wanting to include a major refactor of the README to turn it into cleaner docs.
Enable the use of Units of Measure with the Decision type
It would be nice for the user to be able to "opt-in" to using UoM with the basic building blocks of modeling but still be able to work without them. This would be akin to how
float
andfloat<'Measure>
behaves in F#.It would be nice for the following types to support UoM:
Here is how they should interact:
Function signatures for the
+
operatorThe addition operator should force the UoM to match on both sides of the operator for types where addition is allowed. All of the function signatures are commutative
Function signatures for the
*
operatorThe
*
operator should have the same behaviors around UoM that thefloat
type follows.The Comparison Operators
In Flips
<==
,>==
, and==
are binary infix operators and are used to createConstraintExpression
by comparing two differnetLinearExpression
. The Comparison Operators should enforce UoM when they exist.The challenge is that when we construct the
ConstraintExpression
we have to ensure UoM match. Once we have aConstraintExpression
, we no longer care about UoM. AConstraintExpression
is a element of aConstraint
type and aModel
contains aConstraint list
. Since UoM is a kind of type information we need to drop it or ignore it at some point so that aModel
can have aConstraint list
where theLinearExpression
inside may be of different UoM.