Unitful.jl icon indicating copy to clipboard operation
Unitful.jl copied to clipboard

Matching serialise/deserialise behaviours for units

Open rafaqz opened this issue 5 years ago • 4 comments

Discussed from https://github.com/PainterQubits/Unitful.jl/issues/214#issuecomment-714178239 onwards. Making a separate issue so this is easier to find and track.

We should be able to serialise and deserialise units to strings - so e.g. that a table with a units column can be save and loaded again.The way of doing this suggested by @c42f is to make show output the raw julia version that uparse can read back into units. We would then add print methods and some REPL show methods that keep the current behaviour in normal use.

A PR for this would involve:

  • [ ] swapping current show methods to work with print
  • [ ] define show(::IO, ::MIME"text/plain", unit) so the REPL still looks ok.
  • [ ] write new show methods that return the raw julia version
  • [ ] write some tests for print/string/show behaviours

rafaqz avatar Oct 26 '20 03:10 rafaqz

Just supporting julia syntax for round-tripping and the current syntax for pretty printing would be fine by me. It seems straightforward and explicit.


Continuing some conversation from #214

Solutions I can think of for this are to just parse whitespace as * in uparse leaving show as-is

Currently uparse just uses the Julia parser itself so somebody would have to implement a small parser. That might be fine though.

If we wanted to support a more general syntax, some research I did a while ago in https://github.com/PainterQubits/Unitful.jl/issues/214#issuecomment-572355866, seems relevant. Here's some links to existing prior art for unit string representations:

  • The NIST SP811 is influential but doesn't specify a precise grammar for unit strings.
  • The FITS standard is well developed but "overcomplete" allowing flexible parsing of various non-unique representations of a unit string. It's the standard followed in the astropy.units library and seems to have inspired various other standards and proto-standards such as ASDF which defers to FITS.
  • The MIXF standard seems to be an elaborated version of SP811 for plain text unit strings provides a formal grammar. This seems promising but is rather opinionated about using . as multiplication which means incompatibility with julia syntax.
  • The NetCDF format appears to use the udunits package which has a unit parser with a grammar also derived from SP811 with an extension for affine units.
  • The python pint library seems to use python syntax for unit string constructions

c42f avatar Oct 26 '20 03:10 c42f

This feature is badly wanted! See my comment in https://github.com/PainterQubits/Unitful.jl/issues/412#issuecomment-770627061.

singularitti avatar Feb 01 '21 07:02 singularitti

It shouldn't be too hard to write if you want to have a go at it. I haven't had time yet as this is more something I want to work theoretically than something I need immediately.

rafaqz avatar Feb 01 '21 07:02 rafaqz

@sostock @rafaqz @c42f I am working on this problem (cf. #412, #569). My implementation is based on this proposal, https://github.com/PainterQubits/Unitful.jl/issues/214#issuecomment-714911511 One example is here. Is my approach correct?

function print(io::IO, x::Units) # swaped old show(io::IO, x::Unitlike)
    is_first =true
    sep = " "
    foreach(sortexp(x)) do y
        is_first || print(io, sep)
        showrep(io,y)
        is_first = false
    end
end

show(io::IO, mime::MIME"text/plain", x::Units) = print(io, x) # for REPL

function show(io::IO, x::Units) # for default, parsable
    sep = "*"
    is_first = true
    foreach(sortexp(x)) do y
        p = power(y) 
        pow = p == 1//1        ? ""  :
              p.den == 1       ? string("^", p.num) : 
              p == p.num/p.den ? string("^", "(", p.num, "/" , p.den, ")") :
                                 string("^", "(", p.num, "//", p.den, ")")
        is_first || print(io, sep)
        print(io, prefix(y))
        print(io, usym(y))
        print(io, pow)
        is_first = false
    end    
end

(The content of show(io::IO, x::Units) method is the same as the one in UnitfulParsableString.jl)

michikawa07 avatar Mar 20 '23 23:03 michikawa07