-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Move Unit.js into a standalone npm library #1486
Comments
Sounds like a great idea. Not sure how difficult it would be. I'll give it some thought. |
We could nab the @mathjs namespace on npm for it? |
@harrysarson I guess we could, though I think it will be more powerful if Unit.js would become a really standalone library, and not sort of a child project still coupled to mathjs. |
Sounds like a real challenge, but it feels like the right direction to take it. Where do we go from here? Do we create a new repo? |
I think it involves the following steps:
|
I like the idea of making mathjs into a monorepl with one github repo containing multiple npm packages. Although this approach has downsides too. |
Yes, it's also possible to spin multiple npm packages from the current mono repo. The reason I'm interested in separating Unit.js is that, like say I see the future of mathjs as being an integrated environment for mathematics in JavaScript: a "hub" merging a lot of different data types together (matrices, units, complex number, ...), and an expression parser making it convenient to work with mathematics in JavaScript. So, moving Unit.js (and also the Matrix classes) out of mathjs would help mathjs getting more focused, and will probably lead to an API that also makes it easier to extend mathjs with more data types (like typed arrays). |
That makes a lot of sense 👍 |
Sure, I'll take ownership. Sounds like a fun adventure. That part I'm unsure how to do would be injecting other types like Complex and Decimal into the new units module. |
Cool Eric 😎 There is no need to know all answers beforehand already, one step at a time. Have a look at the Unit.js file in the |
All finished: https://github.com/ericman314/unitmath Just kidding, all it does is output |
I just checked out the dependency mechanism in the Instead, perhaps it would be enough to just allow users (and math.js) to override |
I've written a proposed API for the new library. If you like we can continue the discussion there: ericman314/UnitMath#1 |
@josdejong, I've written a proposed API for extending the new unitmath library with custom types. Do you think that this API would work with mathjs? (https://github.com/ericman314/unitmath#extending-unitmath) |
Man I love it already @ericman314! The API looks really clean and simple. I'm glad you go for immutable and factory functions instead of constructors with new :). I think your approach with custom functions I'm not sure about the name "extendType". maybe call it "arithmetic" or something? Or simply flatten the config, that may be cleaner and easier to remember, like About number/BigNumber/Fraction, etc: I think that the library does not need knowledge about these specific numeric types, but simply allow passing your own I was thinking about having all functions as methods, which doesn't allow for tree-shaking: that may not be an issue in practice since the number of methods is relatively limited and the implementation is small compared to the rest of the library. So please go ahead with chained methods as described in your proposal :) Are you still open for finding a nice fancy funny catchy name for the library? If so I will give it some thought. |
Thanks for the feedback! You are right that flattening the config would be simpler. No need to overcomplicate things. I actually do find it difficult to use nested config options (think chart.js). The cloning implementation would be a lot simpler, too. Maybe prefix each with "custom", to show that it's a custom operator? So, I also thought about a Yes it's too bad about the complex unit I'm definitely open to catchy names. I tried variants of Unit.js but they were too similar to existing libraries. I settled on Unitmath because it hints at what the library does--math with units. It's also not too hard to remember, and it's quick to type. Unfortunately, it is painfully boring. To me, it's OK for a really popular library to have a weird, unrelated name (like electron), but I tend to put more trust in smaller, less known libraries if the name actually says what it does. |
I just altered the capitalization in the docs to UnitMath and I like it much better now. (The package name will remain lowercase.) |
Sounds good going with flat config options. Maybe the "custom" prefixes are a bit superfluous too: any config option you set is per definition custom since it deviates from the default :). I think you're right about It makes sense to simply leave the I totally agree that a boring but clear name is better than something vague. I will give the naming some thought. It's unfortunate that "unit" conflicts with unit testing libraries. |
Version 0.2.1 of UnitMath was just released. It is nearing a stable API. I haven't tested it with custom types yet, but trying to integrate it into Math.js might be the perfect opportunity to put it through its paces. What branch should we be basing this work on? |
Hope this isn't too far off topic for this, but I have a sort of meta custom type that I've put together for dealing with sums of different unit types. I'd love to contribute it back upstream if there's interest. |
Absolutely, you can find the project here: https://github.com/ericman314/UnitMath |
That's awesome news @ericman314 😎 ! You can start a new feature branch based on |
Thanks, I just added ericman314/UnitMath#13 |
@ericman314 v6 is about ready to release, I hope to publish the last beta version today. After that I want to write a blog post about it, work on some nice-to-haves. The real release can be done in one or two weeks from my point of view, but it would be great to have What is the status of |
UnitMath is getting very close. Since mathjs will be its biggest user at first, having it fully integrated and working would be a good indicator that it has reached a stable api. Otherwise I'm afraid it would just increment minor versions forever without reaching v1. I'll need some help integrating it though, as I'm unfamiliar with mathjs@6's new architecture. A couple weeks might be a little fast, but we'll try. |
I have some time on my hands from Friday onwards and happy to help piping UnitMath into mathjs |
Will the new architecture make it possible to define units limited to a
specific scope?
…On Sun, May 26, 2019, 08:47 Harry Sarson ***@***.***> wrote:
I have some time on my hands from Friday onwards and happy to help piping
UnitMath into mathjs
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1486?email_source=notifications&email_token=AAABZHJUNRTTOCNDTBENKTDPXKWKFA5CNFSM4HH52ZY2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWIIKJY#issuecomment-496010535>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAABZHNSPRL5BLGXIZIM5ITPXKWKFANCNFSM4HH52ZYQ>
.
|
And how about something like this? class Unit {
timesTwo () {
return this.mul(options.type.conv('2'))
}
} |
I was thinking about this more:
If math.js tries to multiply a |
I've had the best luck with this for conv: (value) => typeof value === 'string' ? numeric(value, config.number) : value, I think at this point most of the tests are failing due to formatting issues. There are still some problems with implicit conversion of numbers to BigNumbers. Perhaps this could be fixed by using your suggestion, Jos, of recasting all the hard-coded numbers in UnitMath to strings. Built-in units would store their numbers as strings, which |
If you want to go puristic and fully support custom numeric types, I think However, practically seen, most units do not have a precision higher than what can be expressed with a regular I like your latest solution for Do you think |
Yeah I'd like that. I think what I'll try to do is define hard-coded numbers as strings instead. That should hopefully avoid the errors we've been getting converting numbers to Fraction and BigNumbers. Then it could silently convert those strings to the custom type and cache them for a performance improvement. And by default, the conversion function will continue to support either numbers or strings, for maximum flexibility in defining new units. And finally, since the minimum requirement for the conversion function is that it can parse a string, |
Already running into trouble. Consider this function in function denormalize(unitPieces, value, type) {
let result = value
for (let i = 0; i < unitPieces.length; i++) {
unitValue = type.conv(unitPieces[i].unit.value)
unitPrefixValue = type.conv(unitPieces[i].unit.prefixes[unitPieces[i].prefix])
unitPower = type.conv(unitPieces[i].power.toString())
result = type.div(result, type.pow(type.mul(unitValue, unitPrefixValue), unitPower))
}
... Now consider the following test: const unit3 = new Unit(math.bignumber(14.7), 'lbf')
const unit4 = new Unit(math.bignumber(1), 'in in')
const unitD = unit3.div(unit4)
assert(math.isBigNumber(unitD.value)) The error occurs at So after seeing the intricacies involved, I'm a little more hesitant about converting everything to strings. Do you have any other ideas? |
What if we created wrappers for |
Creating wrappers in If working from strings internally gives too much complications, it may be best to just work from numbers internally instead. Or does that not solve these complications? The old |
Just gave it a try, and fewer tests are failing than ever before! We may be on to something. const promoteArgs = (fn, ...args) => {
let types = {}
args.forEach(a => { types[a.type || typeof a] = true })
const numTypes = Object.keys(types).length
// We expect to have these types:
// number
// Complex
// BigNumber
// Fraction
if (numTypes === 1) {
// No alteration is necessary
return fn(...args)
} else if (numTypes === 2) {
// May need to convert one or more of the arguments
if (types.hasOwnProperty('number')) {
if (types.hasOwnProperty('Complex')) {
// Convert all args to Complex
return fn(...args.map(a => typeof a === 'number' ? new Complex(a, 0) : a))
} else if (types.hasOwnProperty('Fraction')) {
// Convert all args to Fraction
return fn(...args.map(a => typeof a === 'number' ? new Fraction(a) : a))
} else if (types.hasOwnProperty('BigNumber')) {
// Convert all args to BigNumber
return fn(...args.map(a => typeof a === 'number' ? new BigNumber(a) : a))
}
}
}
// All valid paths should have returned by now
// Throw this error when debugging, or for more consistent error messages, try it anyway and let typed.js throw
throw new Error('unit.js attempted to perform an operation between the following incompatible types: ' + Object.keys(types).join(', '))
//return fn(...args)
} |
I think for the most part the type problems have been resolved. Do you want pull the changes so far into the |
That's good news! Yes feel free to merge your changes into the |
There was an issue with round-off error due to floating point arithmetic that was then getting proprogated into Fractions, so I caved and added a second parameter to I've pushed all my commits into the
** About the complex comparison method--if we create a separate |
I'm trying the multiple instances of
|
We may indeed need to adjust the unit tests of mathjs to the new way that unitmath works.
That's correct. I was able to remove most circular dependencies in I will have a more thorough look into the open issues you mention coming days, see where I can help. |
Yes I saw that trick you used in |
|
Another equally horrific solution is to make the unit wrapper a state
machine that keeps track of the most recent unit type and then coerce
future arithmetic operations to that type. Like I said, horrific. But if we
rule out all the bad ideas, the only left will be the right answer!
Your other ideas sound good. I think I will have time during the next few
days to work on it.
…On Mon, Jun 3, 2019, 1:29 PM Jos de Jong ***@***.***> wrote:
1.
About conv: it indeed feels dirty to provide this hint as to which
type to convert to, and the promoteArgs function also feels relatively
complicated. I think it will help to first resolve the other open ends, and
when everything more or less works, revisit conv and promoteArgs to
see if we can find a better solution.
2.
About comparison of complex numbers: I can think of a few directions
- Do we really need to compare (complex) numbers, or would it be
enought to uniquely sort them? In case of the latter, we could use
sortNatural from mathjs and pass that via the custom type functions.
- We have to implement wrapper around the custom type functions
comparing numeric values, so that they do "something" meaningful in case of
complex numbers instead of throwing an error.
3.
About toNumeric, toNumber: I think toNumeric is the better version of
toNumber, and we only need one of the two. Basically, we want to have
a way to get the numeric value out of the unit. Does that make sense?
4.
Fraction InvalidParameter: let's do some debugging.
5.
About configuration changes in createUnit: I think the most logical
here is to the factory function optionally dependent on on, which is a
listener that can be used for config changes. Just like
createUnitFunction does already. Search for occurrences of '?on' in
the code base.
6.
unit.exists does work, but the failing unit tests use Unit.exists, and
Unit is a wrapper around unit giving a deprecation warning that the
class Unit doesn't exist anymore. So this should be fixed when
changing the unit tests to use math.unit instead.
I'm not sure if my "trick" to expose the static method exists on every
unit: this probably breaks when you do any operation on the unit, which
returns you a new unit:
// expose static exists functionunit.exists = (singleUnitString) => unitmath.exists(singleUnitString)
This is a trick to get the static method exists exposed on a unit. I
guess that breaks when creating a new unit from the existing unit or so?
One solution would be to have unitmath expose the function also as a
method on every unit. Or else, in SymbolNode where we need it, we need
access to our unitmath instance.
7.
About creating a new unit from an existing unit: If this cannot be
done via the constructor, we could expose the factory function to create a
new unit on every unit, we could call it create(...) for example, like:
const unit = UnitMath.config({ ... })const a = unit('5cm')const b = a.create('2 inch') // create a unit without needing the unit factory function
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1486?email_source=notifications&email_token=ABQNHEPETQAKO5UCSKXNN7LPYVWJRA5CNFSM4HH52ZY2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODW2OE5I#issuecomment-498393717>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABQNHEPTZUR4TSQYN5DSYMLPYVWJRANCNFSM4HH52ZYQ>
.
|
😂 that's the spirit. Yeah it's quite a challenge. It all feels to complicated. Maybe we need some sort of |
Finally had some time to read through and respond to your comments above:
I wonder if a lot of these issues could be addressed by creating a new class in math.js that wraps every method of unitmath. It could expose whatever methods are needed for backwards compatibility. Every method call from anywhere else in the mathjs library gets filtered through this new class, so it can make sure the types are converted correctly. I'm not familiar enough with the new architecture to know the right way to go about doing that, though. |
|
Works for me. I'll add |
Ok cool 👍 |
Any update on this? I'm using mathjs specifically for the unit functionality, and would love to just use a separate library, if possible. |
Hey @jwir3! |
Thanks @m93a that's exactly right. I had hoped to have v1 of UnitMath released long ago but extenuating circumstances prevented this. You can use the current version now, but some of the API may change. Specifically we're trying to simplify how users will configure and customize the library. So if you're just using the built-in units, and not adding any of your own, that functionality probably will not change much. |
UnitMath is now v1.0.0-rc.1, feel free to give it a try. I would love some feedback before we lock in the API at v1.0.0. https://www.npmjs.com/package/unitmath/v/1.0.0-rc.1 |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Just floating an idea here: the
Unit.js
class is a powerful piece of functionality on it's own, separate from math.js. It could be interesting to think about splitting it from math.js into it's own npm library, similar to the libraries Decimal.js, Fraction.js and Complex.js used by mathjs under the hood. Of course Unit.js should keep it's injection of methods likeadd
,multiply
,Complex
, etc to keep it flexible and integrable).What would we gain with it?
@ericman314 do you have some thoughts in this respect?
The text was updated successfully, but these errors were encountered: