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

Adds JNumber conversions #41

Closed
wants to merge 1 commit into from
Closed

Conversation

eed3si9n
Copy link
Contributor

Ref #38

This implementation is loosely based on that of Circe.

JNumber is internally subdivided into package private seven datatypes JStringDecimal, JBigDecimal, JBigInt, JLong, JInt, JDouble, and JFloat corresponding to the input values passed into JNumber.apply(...). This allows performant roundtrip back to the original number types, if needed.

Note that the parser implementer now has an option of constructing JStringDecimal via JNumber(value: String) or doing the number parsing upfront.

This implementation is loosely based on that of Circe.
@codecov-io
Copy link

codecov-io commented Jul 24, 2017

Codecov Report

Merging #41 into master will decrease coverage by 18.61%.
The diff coverage is 23.46%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #41       +/-   ##
===========================================
- Coverage   44.68%   26.07%   -18.62%     
===========================================
  Files           5        5               
  Lines         687     1097      +410     
  Branches      133      170       +37     
===========================================
- Hits          307      286       -21     
- Misses        380      811      +431
Impacted Files Coverage Δ
js/src/main/scala/scalajson/ast/JValue.scala 0% <0%> (ø) ⬆️
...s/src/main/scala/scalajson/ast/unsafe/JValue.scala 0% <0%> (ø) ⬆️
...m/src/main/scala/scalajson/ast/unsafe/JValue.scala 47.29% <39.61%> (-12.22%) ⬇️
jvm/src/main/scala/scalajson/ast/JValue.scala 48.86% <40.6%> (-24.53%) ⬇️
shared/src/main/scala/scalajson/ast/package.scala 16.88% <0%> (-40.89%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 99ee084...5f215c9. Read the comment docs.

@eed3si9n eed3si9n mentioned this pull request Jul 24, 2017
2 tasks
def unapply(value: JNumber): Option[String] = Option(value.stringValue)

private[ast] def jnumberEquals(value: JNumber, obj: Any): Boolean =
(value, obj) match {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing as jnumberEquals might be hot in certain kinds of applications: Could this be expressed in an allocation-free manner (possibly sacrificing a bit of conciseness)?

@sirthias
Copy link

IMHO representing JSON numbers via an ADT like this is the way to go.
What's you rationale for making the sub types package private instead of opening up the ADT altogether?

@mdedetrich
Copy link
Owner

mdedetrich commented Jul 24, 2017

@sirthias I suppose if the internals change then you are not breaking it for end users?

@sirthias
Copy link

IMHO this whole effort is about not changing anything, ever.
We need to get it almost 100% right from the beginning.

This is not a library that receives new features or other upgrades over time.
It's supposed to deliver a minimal and consistent JSON ADT, not more and not less.

That's why simplicity and clarity is quite important in my view. Any special construct, hidden fields, non-case-classes or other "tricks" should be extremely well justified.

@mdedetrich
Copy link
Owner

This is not a library that receives new features or other upgrades over time.
It's supposed to deliver a minimal and consistent JSON ADT, not more and not less.

Completely agree, which is why I am being very careful about a release. Will need @eed3si9n , and possibly @travisbrown to clarify why they are private

@eed3si9n
Copy link
Contributor Author

What's you rationale for making the sub types package private instead of opening up the ADT altogether?

Unlike Json4s's AST where you get either case class JLong(num: Long) or case class JDecimal(num: BigDecimal) representing the actual number value, we have seven that represents how the JNumber was constructed. The reason why it's there is to avoid conversion cost, and it's almost irrelevant to the consumer of the AST.

For example, suppose I am getting "Twitter fave count", and I assume the number can be represented as Long, depending on the parser the number might be stored as JStringDecimal, JBigDecimal, JLong or JInt etc. The only thing I need to be concerned is can I safely convert it to Long.

@eed3si9n
Copy link
Contributor Author

IMHO this whole effort is about not changing anything, ever.
We need to get it almost 100% right from the beginning.

I agree with the general idea, but we should relax this to keeping strict binary compatibility of the exposed classes.

Other goals are correctness, user-friendliness, and performance. Hiding often allows us to do performance tricks without compromising on the usability of the surface API.

* @author Matthew de Detrich
*/
// Due to a restriction in Scala 2.10, we cant override/replace the default apply method
// generated by the compiler even when the constructor itself is marked private
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I can compile my JNumber in 2.10, 2.10 duplication is dropped.

@mdedetrich
Copy link
Owner

@eed3si9n Thanks, I will have a look at this today/tomorrow

@sirthias
Copy link

Other goals are correctness, user-friendliness, and performance. Hiding often allows us to do performance tricks without compromising on the usability of the surface API.

Yes, I fully agree. However, in my view this project is a bit different from ordinary libraries as it provides almost only structure and very little logic (at least I would consider this a goal).
If there's no logic we don't really need to hide anything. Also, if the structure is right there is no need for "performance tricks".

My concern with hiding the internal ADT structure is that there will definitely be people that will want to access the underlying inner structure, if only to perf-optimize certain cases on their layer.

If we come up with right structure anyone who doesn't care about details can simply rely on the JNumber interface. But if I really wanted to I could drop down to the fully specialized JNumber ADT and apply dedicated handling to each case.
(One example that come to mind would be an optics library that provides AST transformation/manipulation facilities from the outside. Not being able to see the real underlying structure could be a disadvantage.)

@eed3si9n
Copy link
Contributor Author

@sirthias After talking with @Ichoran on Gitter, I am coming to the idea that both #38 and this PR have feature creeped.

I think you're right that we should try to keep the AST open, and in our case plain String (with regex check).

@mdedetrich
Copy link
Owner

Regarding #38 , this was an attempt to provide performant JNumber conversions without altering the current API design too much, hence why the JNumber constructor was largely unchanged and we are using a bitflag

@eed3si9n
Copy link
Contributor Author

Closing this in favor of #42

@eed3si9n eed3si9n closed this Jul 27, 2017
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

Successfully merging this pull request may close these issues.

4 participants