Code Monkey home page Code Monkey logo

Comments (36)

keilw avatar keilw commented on September 24, 2024 4

B) No, a factor seems enough.

from unit-api.

keilw avatar keilw commented on September 24, 2024 2

C) Do not standardize Prefix at all and leave the current prefix classes like hey are in the RI or other places.

from unit-api.

keilw avatar keilw commented on September 24, 2024 1

A) Yes

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024 1

Alternatively, no method in Prefix and specialized methods in sub-types only:

  • getPowerOf10() in MetricPrefix
  • getPowerOf2() in BinaryPrefix

Compared to factor, powerOfXXX() avoid rounding errors for very large factors or for any factor smaller than 1.

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

I'm neutral on this question. The use of UnitConverter here is not necessarily an issue; it does not prevent users to use Unit-API in an implementation-independent way. It may prevent mixing two independent implementations, but this is a separated issue. Mixing two implementations is already not well supported, as discussed on units-dev@g….com mailing list on March 9th.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Since a call to unit.multiply(factor) also produces an implementation of Unit, even the factor may not guarantee 100% reusability across multiple implementations, but it sounds appealing to define some of the most basic and widely-used items like SI prefix in the API if we can.
Please note, if we did go fo B and defined MetricPrefix, potentially also BinaryPrefix (open to discussion for that) as "hybrid enums" then offering a default method return Collections.<Prefix>unmodifiableSet(EnumSet.allOf(prefixType.asSubclass(Enum.class))); is a logical result. It makes no sense to declare one or two default prefixes in the API but split that simple line into the RI. If we use enums as prefixes in the API, then it should be in the API as well. A default method is no final, so every implementation may override it if they use something else.

from unit-api.

keilw avatar keilw commented on September 24, 2024

@unitsofmeasurement/experts and contributors like @andi-huber, @filipvanlaenen or @ejberry (especially if the approach had an effect on JVM language bindings like Kotlin) would you mind casting a few votes on this? Thanks.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Keep in mind we have the methods like KILO() (in the RI) or KIBI() (currently in the common library) so we must remain backward compatible.
The only alternative if everyone disagreed on that (currently here we also have 2 votes for a getValue() or getFactor() approach similar to UCAR Prefix) was to drop the idea of Prefix in the API entirely and have implementation-specific approaches that may either use UnitConverter, a numeric factor or a combination of both. So just in case there was a huge majority towards not standardizing the Prefix at all I add option:

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

Not sure what is the compatibility issue with KILO, etc. methods in existing implementation? You want the RI to compile without any change?

from unit-api.

keilw avatar keilw commented on September 24, 2024

Although the package name is different yes. And people expect something like KILO(GRAM) or kilo(GRAM) - we picked uppercase because the method represents a constant factor - not getPowerOf10(8, GRAM) or similar, that would just feel totally odd and alienate users.
Even quantity which is a "marker interface" also has a couple of methods, thus a Prefix that is no more than a tagging interface feels wrong. And we might as well drop the idea of standardizing it alltogether. Hence option C for those who think it should not be standardized.

While I haven't been in an EG call for JSR 382 in a while (now the client does not need configuration on the JVM but other things like API security) it seems the JSR also has many similar detail questions to be discussed. However being a 2.x release the backward compatibility is important here. If introducing a Prefix was to break existing code rather than add a new feature, then let's abandon the idea and focus o other topics.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Right now one option of #85 has a majority (even if we counted multiple up- or down-votes instead of just one per participant) but let's try to answer this, because option C here would make #85 completely irrelevant. And others e.g. allowing to declare it in the API here would also influence which alternative is viable or not.

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

I agree that users should be able to recompile their code with no change, but is it really required for implementors? Not all standards have this requirement (e.g. JDBC, JDK tools).

Anyway, Prefix was not part of previous Unit-API. So even implementations will not have anything broken by the proposed alternatives.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Yeah but that's not the point, a method like toThePowerOf10() sounds too strange and mathematical. Those who were in JSR 275 remember when folks like Josh Bloch also voted against it because aspects of it like "SI" sounded too much like "Sheldon Cooper" to them and no what they expected in every day usage. Even while getting some inspiration from BigDecimal and the JDK we may not want to introduce methods likeulp()here. Some JSRs like Servlet have a few default classes or even a few more. Others like CDI or JSON-P have just accessor and service loader facades and no other concrete classes.

from unit-api.

dautelle avatar dautelle commented on September 24, 2024

Following my previous comment on #85
I would recommend not having a Prefix class in the API but two utility classes "Metric" and "Binary".
It is expected that prefixes will be used through static import, e.g.
import static javax.measure.Metric.*; Unit<Length> km = KILO(Meter);

from unit-api.

keilw avatar keilw commented on September 24, 2024

@dautelle Thanks, if we restrict them to using multiply like JSR 108 did, or the uom-lib-common binary prefix, then it could work to keep those in he API. Would you vote for C) here then, it suggests not declaring the Prefix and leave or put concrete classes elsewhere (those appropriate may also be in he API)

from unit-api.

dautelle avatar dautelle commented on September 24, 2024

Yes, but I would recommend adding to the API both "Metric" and "Binary" utility classes (with static methods only).

from unit-api.

filipvanlaenen avatar filipvanlaenen commented on September 24, 2024

The thing that I reacted to the most was that you're returning a UnitConverter as a response to a getConverter() method on a Prefix object. I would expect a PrefixConverter :-)

Maybe this is an indication that the naming for the UnitConverter class is wrong. I checked its definition, and it doesn't seem to be related to Unit at all. While we're at it, I had wished there was a compareTo method on the (Unit)Converter class, so I could figure which Converter/Unit/Prefix was larger (e.g. while trying to implement addition).

from unit-api.

keilw avatar keilw commented on September 24, 2024

How would a PrefixConverter be different from a UnitConverter to justify yet another API or SPI element? The name historically started with JSR 275 (108 only called it something like Converter AFAIR) and to be honest, it is a little bit misleading because the result of the conversion is a number, not a Unit. It does however convert those numbers from one Unit to another, so in the end it's not that wrong and part of the 1.0 standard so it'll likely stick around for some time.

from unit-api.

filipvanlaenen avatar filipvanlaenen commented on September 24, 2024

The name UnitConverter starts to become wrong when it is also used to convert Prefixes. But it's not a big deal.

About the actual issue, I've voted for option B now.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Because it converts numbers from one Unit to another it worked well to get that numerical factor, but if we're able to use the factor directly for all prefixes that we imagine useful, then it is not necessary here. Thanks for voting. I copy/move the two default prefixes to the SPI package. Let's take it from there, at the moment option B (keep the Prefix interface but replace UnitConverter with a numeric value/factor) has a solid majority.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

When replacing UnitConverter with a numeric value/factor I'd suggest to have a closer look at what this factor really is:

  1. For MetricPrefix and BinaryPrefix it is BASE^POWER with both BASE and POWER being (mathematically speaking) integers. So the prefix factor can be defined precise, without resorting to a floating point representation. That way also ONE (the multiplicative identity) is very well defined.
  2. In rare cases a floating-point representation might be required.

Any implementing library that does Prefix calculus (eg. multiplication) will in case of 1) when having access to BASE and POWER be able to do this with arbitrary precision.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

So I'd propose this as most likely sufficient:

public interface Prefix {
  public String getSymbol();
  public int getBase();
  public int getPower();
}

from unit-api.

keilw avatar keilw commented on September 24, 2024

BigDecimal or BigInteger have the a pow() method, so with a Number factor (maybe "value" is the better name) it holds something like TEN.pow(24). We are restricted to the algorithmic operations of Unit, which also has a pow() method, but that won't take the base into consideration. BigDecimal or BigInteger are fairly precise, shouldn't that be sufficient or am I missing something?

Being able to represent both cases was a reason why UnitConverter got applied. It allows multiplication by a floating point value like double or BigDecimal as well as the RationalConverter which contains the base and power element, but directly on the Unit. Actually BigDecimal or BigInteger did not work, but Math.pow() seems to work, have to compare the API level prefix with the ones before in Indriya, there JUnit tests should demonstrate if the factor works or not.

It seems calling it Factor was maybe not such a bad guess.
https://en.wikipedia.org/wiki/Metric_prefix contains a column with a Power operation but also the numeric factor that's a result of that operation, so storing a number might be enough.

I'm afraid however, RationalConverter in the RI or other implementations is not so easy to replace.
After @filipvanlaenen added a patch to Indriya certain multiply operations of a Unit automatically result in the RationalConverter while other values get handled by MultiplyConverter. Especially the biggest prefixes exceed Long, so they do not fall in the RationalConverter automatism.
I am not sure, if it'll work without the prefix taking care of the UnitConverter.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

What if we add one operation to Unit (and Quantity) such that

import static javax.measure.test.unit.DistanceUnit.m;
...
final Unit<Length> km = m.transform(KILO.getConverter());

becomes

import static javax.measure.test.unit.DistanceUnit.m;
...
final Unit<Length> km = m.prefix(KILO);

where the implementation takes care of the prefix calculus?

Redefine Prefix

public interface Prefix {

	/**
	 * Returns the symbol of this prefix.
	 *
	 * @return this prefix symbol, not {@code null}.
	 */
	public String getSymbol();

	/**
	 * Base part of the associated factor in base^exponent representation.
	 */
	public int getBase();

	/**
	 * Exponent part of the associated factor in base^exponent representation.
	 */
	public int getExponent();

}

Extending Unit

public interface Unit<Q extends Quantity<Q>> {
  
  ...
  
  Unit<Q> prefix(Prefix prefix);
}

Implementing Prefix

public enum TestPrefix implements Prefix {
	MEGA("M", 10, 6),
	KILO("k", 10, 3);

	private final String symbol;
	private final int base;
	private final int exponent;

	/**
	 * Creates a new prefix.
	 *
	 * @param symbol
	 *          the symbol of this prefix.
	 * @param base
	 *          part of the associated factor in base^exponent representation.
	 * @param exponent
	 *          part of the associated factor in base^exponent representation.
	 */
	private TestPrefix(String symbol, int base, int exponent) {
		this.symbol = symbol;
		this.base = base;
		this.exponent= exponent;
	}
	
	...
}

Implementing Unit

public abstract class TestUnit<Q extends Quantity<Q>> implements Unit<Q> {

	...

	public Unit<Q> prefix(Prefix prefix) {
		final MultiplyConverter converter = 
				new MultiplyConverter(Math.pow(prefix.getBase(), prefix.getExponent()));
		return this.transform(converter);
	}
}

from unit-api.

keilw avatar keilw commented on September 24, 2024

@unitsofmeasurement/experts @filipvanlaenen @andi-huber Hi, please have a look at the 7 broken JUnit tests Indriya still has after the change to a numeric factor: https://circleci.com/gh/unitsofmeasurement/indriya/405

Especially

 PrefixTest.testNano:137 expected: <1.0E9> but was: <9.999999999999999E8>
  PrefixTest.testNano2:148 expected: <1.0E9> but was: <9.999999999999999E8>
  PrefixTest.testSingleOperation:161 expected: <µg> but was: <g/1000000.0>

All related to NANO conversion look like a precision issue. Since the prefix() operation @andi-huber proposed above has the same result as the pow() inside MetricPrefix at the moment, it would not make a difference if we declare it as base and exponent.
It may help to somehow apply the same RationalConverter as the old MetricPrefix did underneath the hood.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Except for two (that depend on BinaryPrefix from the SPI) all others work after I added the original RationalConverter back to the RI MetricPrefix. It is the only converter that works with the necessary precision. Whether the Unit.prefix() idea is feasible I can't say, because it would force all operations to change to a "reverse notation" of GRAM.prefix(KILO) instead of KILO(GRAM).
I cannot say, if this is a good idea and we also have to make sure that life and usually rather natural expressions in JVM languages like Kotlin or Groovy are not broken or turned into a weird unexpected form.

Actually it may work if we wrapped such a new or updated operation like
Unit<Q> prefix(Prefix prefix) or even call it Unit<Q> transform(Prefix prefix) inside a concrete prefix:

  public static <Q extends Quantity<Q>> Unit<Q> KILO(Unit<Q> unit) {
    return unit.prefix(KILO);
  }

or unit.transform(KILO) whatever we might call it.

What had to happen as a logical result is, that javax.measure.spi.Prefix became javax.measure.Prefix because Unit must not have dependencies on something in an optional package like SPI.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

I've created forks of unit-api [1] and indriya [2] to demonstrate, how proposed base/exponent implementation could look like. I also introduced a new UnitConverter namely PowerConverter [3] specifically to do prefix calculus.

[1] https://github.com/andi-huber/unit-api
[2] https://github.com/andi-huber/indriya
[3] https://github.com/andi-huber/indriya/blob/master/src/main/java/tech/units/indriya/function/PowerConverter.java

from unit-api.

keilw avatar keilw commented on September 24, 2024

Hi @andi-huber, Thanks. Do they all pass JUnit tests like in the master branch right now? (actually the 2 disabled ones for BinaryPrefix should also pass if the new converter works as intended)

If they do, why don't you propose a PR? I know it has dependencies on the API so please propose both.

One thing in the API, please put Prefix into javax.measure, not just the two concrete enums. As you introduced a dependency to Prefix in Unit it must be in the same top level package. Similar to default implementations of e.g. javax.servlet.Servlet these classes make most sense in the core module and package as well. In this case compatible implementations MAY add further prefixes, but those that are fine with MetricPrefix won't have to implement the interface.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

Thanks @keilw ! I'm still working on 15 JUnit Tests failing. These seem String Formatting related.

If I manage to get these fixed, I'll propose a PR.

from unit-api.

keilw avatar keilw commented on September 24, 2024

Great, thanks a lot for your help. If any of those did not work, a "dividend/divisor" approach for RationalConverter would because in master (with patch of MetricPrefix) all relevant tests are green.

from unit-api.

andi-huber avatar andi-huber commented on September 24, 2024

I managed to get all JUnit Tests positive, except for one I have disabled now:
PrefixTest.testSingleOperation(): both Units have different UnitConverters, check for equality will always fail; is related to PrefixTest.testNestedOperationsShouldBeSame() also being disabled.

Today was my first look at indriya and I think the UnitConverter's composition mechanics needs some robust redesign. But I guess this is related to this discussion at:
unitsofmeasurement/uom-se#164

Also I marked AbstractUnits.IDENTITY deprecated to double check, whether its always used correctly.

Other than that great work everyone!

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

@andi-huber is right, the use of base and exponent allow accurate arithmetics where the use of double is subject to rounding errors. Andi's proposal is same than mine and for the same reasons, except that he declares the base and exponent as two separated properties while my proposal has the base implied by the Prefix subtype (all metric prefixes use base 10; all binary prefixes use base 2). I have no strong preference between those two alternatives.

I would not promote the use of BigDecimal as a replacement for (base, exponent) properties. Knowing the base and exponent give more possibilities to perform efficient and accurate arithmetic, and those information are not easy to guess from a BigDecimal. On the other side, it is much easier to compute a BigDecimal from a base and exponent if someone wants to.

from unit-api.

keilw avatar keilw commented on September 24, 2024

I'm not sure about BigDecimal (could a base or exponent be a floating point number) but I considered BigInteger instead of int or at least long. Trying to calculate a single factor in the API already turned out to be tricky, I also tried a single factor, but it had side-effects on existing converters in the RI, so @andi-huber's approach harmonized better with those.

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

@andi-huber's approach is fundamentally same as mine, that you rejected earlier in this thread (but admittedly Andi did the right thing: providing code to prove that the base + exponent approach is a good way to work).

In the MetricPrefix class, the base field can be removed and the getBase() method can return the hard-coded 10 value. This is what was implied by my getPowerOf10() method (but Andi's way is all right - I'm not asking for change)

In the BinaryPrefix class, the base field can be removed and the getBase() method can return the hard-coded 1024 value. This is what was implied (with a different base) by my getPowerOf2() method. The method naming may not have been ideal, but I have no strong opinion on those one - the important thing is the base (implied or explicit) + exponent (or power) approach.

The use of BigInteger instead of BigDecimal does not improve anything; the same objections than for BigDecimal still apply (hard to guess what was the original base and exponent).

from unit-api.

keilw avatar keilw commented on September 24, 2024

The special names for things like powerOf10() vs. others is what I found unpractical. A little bit like the many ofMillis(), ofDecades() etc. in JodaTime/JSR 310. Since base and exponent together create the much larger number, I guess leaving them int or double (at least Math.pow() has double arguments) looks sufficient there.

from unit-api.

desruisseaux avatar desruisseaux commented on September 24, 2024

Yes of course having base and exponent as int is sufficient.

from unit-api.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.