Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about measurements #518

Open
tonicebrian opened this issue Feb 2, 2022 · 2 comments
Open

Question about measurements #518

tonicebrian opened this issue Feb 2, 2022 · 2 comments

Comments

@tonicebrian
Copy link

Sorry if this is not the place but I haven't found a community for asking this question.

My use case is that I have "measurements" that could be either "units", "kg" or "m2" for items, so I have the tuple (item, measurement).

I haven't found a clean way of holding this other than creating a sum type called Measurement and having:

sealed trait Measurement {
  val quantity: Quantity[_]
  def unfold: (Double, String)
  def getNormalizedValue: Double = this.unfold._1
}

case class UnitMeasurement private (quantity: Dimensionless) extends Measurement {
  override def unfold: (Double, String) = quantity toTuple Each
}
case class AreaMeasurement private (quantity: Area) extends Measurement {
  override def unfold: (Double, String) = quantity.toTuple(SquareMeters)
}
case class MassMeasurement private (quantity: Mass) extends Measurement {
  override def unfold: (Double, String) = quantity toTuple Kilograms
}

object Measurement {
  def apply(quantity: Double, unit: String): Measurement = unit match {
    case "kg" => MassMeasurement(Kilograms(quantity))
    case "m²" => AreaMeasurement(SquareMeters(quantity))
    case "ea" => UnitMeasurement(Each(quantity))
    case _    => throw new IllegalArgumentException(s"Unknown unit: $unit")
  }
}

But now I want to have a MeasurementDimension in my anticorruption layer and I'm facing the same problem, creating a new sum type. All this boilerplate looks like I haven't understood properly the type hiearchy of squants.

Am I overengineering? What would be a cleaner approach to work seamlessly with just 3 disparate quantities?

@garyKeorkunian
Copy link
Contributor

It looks like your trying to discover the quantity type from the unit symbol.  That creates a problem for the type system.

Normally squants would parse a string for a particular quantity type

val mass: Mass = Mass("1.0 kg")

but you would like to have something generic

val something: = parseQuantity("1.0 kg")

and it would return the correct type.  However, what type is something?  That should be known at compile time.  It can't be Quantity[Any] as the type parameter must be derived from Quantity (self-typing).

And while this might be useful for the general handling of value/unit pairs, it breaks the general principle of enforcing dimensional analysis type rules.  If we somehow can treat something as a "generic" quantity, what can I do with it?  Which units can it be converted to, and which operations can it participate in.  To do that, I need to know that something is a Mass

That's also true when accepting input from a user.  If they're supplying a quantity of mass, we should know that.
 

I am interested in your use case, particularly if I am missing the point.

@tonicebrian
Copy link
Author

tonicebrian commented May 31, 2022

I think you understood the problem I have and partly answered it cannot be done. My use case is that I need to parse a CSV containing a bill of materials with a description and a quantity so I wanted to have something like:

type BoM = List[(String, Quantity[???])]

So basically I want a container of quantities (in my case quantities will only be of kind Mass, Area and Unit) but later I want to group them by dimension. But as you say, having Quantity[Any] is not useful because then I cannot do operations. In my use case I have prices per unit, so I'd like to group all units of the same dimension and then compute prices.

That's the reason of creating Measurements sum type to unify things, but my feeling was that it is a lot of boilerplate and/or that I'm not exercising squants to its fullets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants