goldfirere / units Goto Github PK
View Code? Open in Web Editor NEWThe home of the units Haskell package
The home of the units Haskell package
Right now, users get a missing signature warning.
Since 806a27d (issue #22), the evalType
TH function is available to evaluate types as far as possible. This works well for, e.g., Mass
and Length
. However, the following instance fails:
instance Show $(evalType [t| Volume |]) where
Giving the following error:
evalType [t| Volume |]
======>
Qu ((@*) ((GHC.Types.:) (F Data.Metrology.SI.Dims.Length (S Zero)) GHC.Types.[]) (S (S (S Zero)))) DefaultLCSU Double
src/NutriDB/AbsSyn.hs:68:10:
Illegal type synonym family application in instance:
Qu
('['F Data.Metrology.SI.Dims.Length ('S 'Zero)]
@* 'S ('S ('S 'Zero)))
'DefaultLCSU
Double
In the instance declaration for
‘Show (Qu ((@*) ((:) (F Data.Metrology.SI.Dims.Length (S Zero)) []) (S (S (S Zero)))) DefaultLCSU Double)’
Failed, modules loaded: NutriDB.DefaultValue.
In fact, all types defined using the :/
combinator (and possibly others, such as :^
) fail with the same error. While these types are indeed more complex than Mass
and Length
, they at first glance don't appear to be very polymorphic, suggesting that it may be possible to use evalType
with types such as Volume
as well.
{this is not a time-critical issue for me}
This is done in an IHaskell notebook using ghc 7.8.4, with units 2.21 and units-defs 2.0. Am I doing something wring here with the Show
instance?
:set -XTypeFamilies
import Data.Metrology
import Data.Metrology.SI
import Data.Metrology.Show
-- From http://en.wikipedia.org/wiki/Astronomical_unit
data AstronomicalUnit = AstronomicalUnit
instance Unit AstronomicalUnit where
type BaseUnit AstronomicalUnit = Meter
conversionRatio _ = 149597870700
d = 9.2e-27 % (Kilo :@ Gram :/ (Meter :^ sThree))
I can then convert this into a different set of units (picked to give a reasonably-sized number rather than that it is any-way useful!):
d # (Kilo :@ Ton :/ (AstronomicalUnit :^ sThree))
The output of this conversion is
30.800946577458888
Now if I add in a Show
instance:
instance Show AstronomicalUnit where
show _ = "au"
Re-evaluating the conversion results in an error:
d # (Kilo :@ Ton :/ (AstronomicalUnit :^ sThree))
Could not deduce (Unit AstronomicalUnit) arising from a use of ‘#’
from the context (Fractional n) bound by the inferred type of it :: Fractional n => n at :1:1-49
In the expression: d # (Kilo :@ Ton :/ (AstronomicalUnit :^ sThree))
In an equation for ‘it’: it = d # (Kilo :@ Ton :/ (AstronomicalUnit :^ sThree))
See how unreadable type errors are now:
Couldn't match type ‛units-1.1:Data.Dimensions.DimSpec.Reorder
d20
'['units-1.1:Data.Dimensions.DimSpec.D Gram ('P 'Zero),
'units-1.1:Data.Dimensions.DimSpec.D Meter 'Zero,
'units-1.1:Data.Dimensions.DimSpec.D Second One]
units-1.1:Data.Dimensions.DimSpec.@@- '['units-1.1:Data.Dimensions.DimSpec.D
Gram ('P 'Zero),
'units-1.1:Data.Dimensions.DimSpec.D
Meter 'Zero,
'units-1.1:Data.Dimensions.DimSpec.D
Second One]’
with ‛'['units-1.1:Data.Dimensions.DimSpec.D Gram One,
'units-1.1:Data.Dimensions.DimSpec.D Second One]’
The type variable ‛d20’ is ambiguous
It would be nice for users to be able to define just units -- not dimensions -- and get to work. This interface would be exported from Data.Metrology.Mono. My guess is that it will require Template Haskell.
If I change units.cabal
to depend on singletons-th
I get a bit further, but then I get:
Data/Metrology/Z.hs:81:2: error: Cannot find type annotation for ==
|
81 | $(singletons [d| data Z = Zero | S Z | P Z deriving Eq |])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Data/Metrology/Z.hs:81:2: error: Q monad failure
|
81 | $(singletons [d| data Z = Zero | S Z | P Z deriving Eq |])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At this point I'm not sure what to do
On writing the test for #31: (please look at comments in https://github.com/goldfirere/units/blob/309236af6dcc6aae4da1bb4b2c7bab91534b6614/Tests/Vector.hs) ,
I noticed that the current design of Data.Metrology.Vector
requires Fractional
instance for V n
where V
is the vector type constructor. Particularly, in this case Fractional (Rational, Rational)
. I think such instances are not generally available.
From this viewpoint, it seems more natural to define and operate over types of the form V (Qu d l n)
, rather than Qu d l (V n)
.
This problem is important to me. Because I'm going to do physical simulations using units
, I inevitably deal with array of quantities. I've been thinking about this for some time. But just thinking. Thank you for bringing up this issue as a concrete problem with codes!
Hi, bit of an odd "issue", but please bear with me!
We've just started using units
in a computer vision application, and almost all the measurements we have are two-dimensional vectors. Looking at https://github.com/goldfirere/units/blob/master/Tests/Compile/Simulator.hs, it's clear that the intention is to represent entire vectors an quantities, but it felt more natural for us (when using linear
) to have Qu
under the vector itself. That is...
velocity :: V2 Length
Is there a fundamental reason why this is a bad idea? It seems that with this approach, Data.Metrology.Linear
isn't necessary at all (except for maybe providing a few orphan instances), as Linear
is already sufficiently polymorphic to support units
.
The vector-spaces package defines several classes that may be useful to us, especially if we want to allow users to assign a dimension/unit to a vector, instead of a scalar. This might be low priority right now, but I wanted to document the idea.
Using stack to install I get the following error:
-- While building custom Setup.hs for package units-2.4 using:
/Users/xavier/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-osx/Cabal-2.0.1.0 build --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
Logs have been written to: /Users/xavier/Personal/haskell-sandbox/.stack-work/logs/units-2.4.log
Configuring units-2.4...
Preprocessing library for units-2.4..
Building library for units-2.4..
[ 1 of 19] Compiling Data.Metrology.Set ( Data/Metrology/Set.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Set.o )
[ 2 of 19] Compiling Data.Metrology.Z ( Data/Metrology/Z.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Z.o )
[ 3 of 19] Compiling Data.Metrology.Factor ( Data/Metrology/Factor.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Factor.o )
[ 4 of 19] Compiling Data.Metrology.LCSU ( Data/Metrology/LCSU.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/LCSU.o )
[ 5 of 19] Compiling Data.Metrology.Dimensions ( Data/Metrology/Dimensions.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Dimensions.o )
[ 6 of 19] Compiling Data.Metrology.Units ( Data/Metrology/Units.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Units.o )
[ 7 of 19] Compiling Data.Metrology.Qu ( Data/Metrology/Qu.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Qu.o )
[ 8 of 19] Compiling Data.Metrology.Unsafe ( Data/Metrology/Unsafe.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Unsafe.o )
[ 9 of 19] Compiling Data.Metrology.Show ( Data/Metrology/Show.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Show.o )
[10 of 19] Compiling Data.Metrology.Combinators ( Data/Metrology/Combinators.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Combinators.o )
[11 of 19] Compiling Data.Metrology.Validity ( Data/Metrology/Validity.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Validity.o )
[12 of 19] Compiling Data.Metrology.Linear ( Data/Metrology/Linear.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Linear.o )
[13 of 19] Compiling Data.Metrology.Internal ( Data/Metrology/Internal.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Internal.o )
[14 of 19] Compiling Data.Metrology.Vector ( Data/Metrology/Vector.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Vector.o )
[15 of 19] Compiling Data.Metrology.Poly ( Data/Metrology/Poly.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/Poly.o )
[16 of 19] Compiling Data.Metrology.TH ( Data/Metrology/TH.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/Data/Metrology/TH.o )
/private/var/folders/mx/91rymfz14gl9r0ffxhf2j9d8000152/T/stack36007/units-2.4/Data/Metrology/TH.hs:222:69: error:
• Couldn't match type ‘Type’ with ‘DerivClause’
Expected type: [DerivClause]
Actual type: [Type]
• In the sixth argument of ‘DataD’, namely ‘(map ConT derivs)’
In the expression:
DataD ct name tvbs Nothing cons (map ConT derivs)
In an equation for ‘mkDataD’:
mkDataD ct name tvbs cons derivs
= DataD ct name tvbs Nothing cons (map ConT derivs)
|
222 | mkDataD ct name tvbs cons derivs = DataD ct name tvbs Nothing cons (map ConT derivs)
|
Sorry I'm too new to haskell to be much help in fixing :(
It might be better to have
deriving instance (d ~ '[], Num d) => Num (Qu d l n)
or something similar to get better inference.
I want this to work:
ratio :: (d1 @~ d2) => Qu d1 l n -> Qu d2 l n -> n
ratio x y = (x |/| y) # Number
But it doesn't, because d1 @~ d2
does not imply that Normalize (d1 @- d2) ~ '[]
. Perhaps a different definition of @~
would work.
Apologies for the somewhat sloppy issue title, it's hard to summarise this in a single sentence!
I'm using units
to build a pricing model for some work we do at CircuitHub. Namely, I'm interested in a pricing model grounded by a sensible real-world interpretation. units
works nicely for this - I introduced a new Cost
dimension (with $
unit), and now I can say things like MkQu_DLN (Cost :/ Area)
- nice!
However, a lot of our costs are actually per some specific unit. For example, parts prices expressed as unit prices. Currently, these are MkQu_DLN Cost
, but they are really cost per something - and here my modeling is a bit stuck.
I could introduce a new PartQuantity
dimension, but this idea of a count seems to keep cropping up. It seems natural to me to have a
data NumberOf x
dimension, such that I can have things like partUnitCost :: MkQu_DLN (Cost :/ NumberOf Part)
.
I'm curious if this really fits in with the spirit of units
though. In particular, it seems like a strange dimension, as it doesn't really have any units, other than an exact count of the things in question. On the other hand, it does a very good job of making sure I scale a per-unit part cost by the actual number of parts I need to order!
What do the others think?
See how the units are rendered in The Solar physics sample article .
The Solar luminosity is 3.839e30 (kg * m^2)/s^3, or 3.839e30 W.
We'd like to utilize the LaTeX's ability and represent units more beautifully, using superscripts and special unit symbols (e.g. \AA
for angstrom and \mu
for micro.)
I think a way to do this is to define Render instances for specific unit types in units-defs
and for unit combinators.in units
. There can be other ways with other benefits. For example we may not want units
to depend on HaTeX
package. We can permit orphan instances. I'd like to listen for better ways.
A few minutes ago, I wanted to know how many yards there are in a mile. So, I said this:
(1 % Mile) # Yard
In response, I got complaints about an ambiguous LCSU. That makes enough sense, but it sure is exasperating for a user just taking this for a quick spin.
I propose new operators %%
and ##
that are monomorphic in their LCSU (requiring a DefaultLCSU) to make the above easier. We would export these from Data.Metrology.Mono and could encourage first-time users to try the mono functions first.
See also issue goldfirere/units-defs#2, which discusses related changes to the structure of the SI modules to support monomorphic programming.
Maybe this is a terrible idea; I'd appreciate feedback. I think I want instances of all the Functor-related classes for Qu d l
. Some uses would be semantically strange but still useful; for instance, Qu Length l (Int -> Float)
is a funny-looking type but I guess it's a function that, given a length as an Int, returns a (possibly different) length as a Float. Obviously most of the library can't operate on such a type, but you'd use it with Applicative's <*>
as an intermediate value on the way to something that the library can operate on.
My immediate use cases are that:
fmap fromIntegral
to convert them to Float and then maybe pick a new LCSU.The (%)
needs its operand on the left side to be an instance of VectorSpace
because it scales it to the canonical unit. But point quantities cannot be scaled (I don't know enough geometry to say if that makes sense).
I hope I haven't forgot any parentheses this time! 😄
Example:
λ> import Data.Metrology.Vector
λ> let p = Point (0::Double, 1::Double)
λ> p
Point (0.0,1.0)
λ> :i p
p :: Point (Double, Double) -- Defined at <interactive>:3:5
λ> import Data.Metrology.SI.Poly
λ> let p' = p % Meter
<interactive>:6:12:
Could not deduce (Data.VectorSpace.VectorSpace
(Point (Double, Double)))
arising from a use of ‘%’
from the context (Elem
Meter
'[CanonicalUnit'
(BaseUnit (Lookup Data.Dimensions.SI.Length lcsu))
(Lookup Data.Dimensions.SI.Length lcsu)],
Elem
(CanonicalUnit'
(BaseUnit (Lookup Data.Dimensions.SI.Length lcsu))
(Lookup Data.Dimensions.SI.Length lcsu))
'[Meter],
Unit (Lookup Data.Dimensions.SI.Length lcsu))
bound by the inferred type of
p' :: (Elem
Meter
'[CanonicalUnit'
(BaseUnit (Lookup Data.Dimensions.SI.Length lcsu))
(Lookup Data.Dimensions.SI.Length lcsu)],
Elem
(CanonicalUnit'
(BaseUnit (Lookup Data.Dimensions.SI.Length lcsu))
(Lookup Data.Dimensions.SI.Length lcsu))
'[Meter],
Unit (Lookup Data.Dimensions.SI.Length lcsu)) =>
Qu
'['F Data.Dimensions.SI.Length One] lcsu (Point (Double, Double))
at <interactive>:8:5-18
In the expression: p % Meter
In an equation for ‘p'’: p' = p % Meter
There is some work to do to make imperial units releasable:
The parser for unit expressions in Data.Metrology.Parser.Internal
is very nice, and I think it could be useful independently from units
. (Full disclosure: I'm writing my own units of measure library, based on experiments with GHC typechecker plugins, and I'd much rather steal your parser than write my own!) Would you be receptive to a pull request if I create a tiny package (perhaps units-parser
) and adapt units
to work with it instead? My intention would be that all the TH-related functionality remain in units:Data.Metrology.Parser
and only parseUnit
and its dependencies move.
At the same time, I'd like to introduce a small generalization: rather than requiring a finite map from strings to units, we could take type UnitTable u = String -> Maybe u
. This would allow my library to represent units as strings, and not determine the list of units in advance of invoking the parser (but obviously there would be no ambiguity check in such a case). The existing interface could be supported except that SymbolTable
would lose its Show
instance. Does this sound reasonable?
Compare:
Currently Eq
and Ord
instances are only defined for dimensionless quantities:
deriving instance Eq n => Eq (Qu '[] l n)
deriving instance Ord n => Ord (Qu '[] l n)
I'm wondering why this can't be changed to quantities of any dimension:
deriving instance Eq n => Eq (Qu d l n)
deriving instance Ord n => Ord (Qu d l n)
These operators would still be less general than |==|
, |<|
, etc., because they don't do normalization with @~
. But they'd be quite useful nevertheless, when using other functions that expect an Eq
or Ord
instance.
As far as I can tell this should be perfectly safe since the dimensions of the two compared quantities will be exactly the same.
We don't have to do this now, but I don't want to forget!
Say we want
instance ToJSON (Carbs Volume) where ...
instance ToJSON (Carbs Mass) where ...
We're stuck, because Volume
and Mass
expand to types that mention type families (specifically, DimFactorsOf
). Yet, the code above is quite reasonable. This is directly related to GHC#8828, which asks, essentially, for code like the above to work. But, I've decided (and SPJ agrees) that GHC#8828 is a bad idea, for other reasons.
What to do? Of course, wrapping in a newtype would work. Are there other ways? A few minutes' thought says no.
(Interesting, but ultimately failed idea: It seems straightforward(-ish) to write deepseq's force
function at the type level, which would evaluate all type family applications in a type. The problem is that there's no way to get the evaluation started. Even if we had type Volume = Force (MkQu...)
, there's nothing forcing the RHS of the type synonym. I can't come up with a way around this. Any GHC feature request that comes to mind seems to be a stand-in for proper type pattern synonyms, as requested in the revised GHC#8828.)
Leaving this open as a ticket to either come up with a solution or to document a workaround.
Consider this:
*Main> (4 % Hertz :: Frequency)
4.0 1/s
That output is suboptimal.
Right now,
import Data.Units.SI
import Data.Units.US
do units <- allUnits
makeQuasiQuoter "u" [] units
fails with a non-letter somewhere. There are multiple problems.
True
to isLetter
. In any case, it's not working.I have no idea if the problem is in this package, in units-parser
or in units-defs
.
It looks to me like the units package doesn't currently support converting between units that have different zero-points, such as between degrees Kelvin, Celsius, and Fahrenheit. How could that work?
With respect to the following update:
20100a6#diff-55b7a1b2fff6092d774c2e61be91ca57R236
Should scalar product normalize the dimension of quantities?
-- | Multiply a quantity by a scalar from the left
(*|) :: Num n => n -> Qu b l n -> Qu (Normalize b) l n
a *| (Qu b) = Qu (a * b)
The type signature used to be: (*|) :: Num n => n -> Qu b l n -> Qu b l n
.
First of all, let me apologize my carelessness if the change was necessary to the updates recently made.
If I have choice, I'd prefer the older type signature that doesn't normalize the type.
I used the fact that the scalar product preserve the quantity type quite a lot in my research. For example, the following code makes some linear combination of function according to air composition:
airMix :: (ChemicalSpecies -> Qu d l Double) -> Qu d l Double
airMix func = 0.78 *| func N2 |+| 0.21 *| func O2 |+| 0.01 *| func Ar
Well, I was able to update the physics code by adding type constraint such as d ~ Normalize d
. Then I need the new constraint everywhere I use airMix
. In some sense, it can be good convention to encourage keep dimensions normalized.
In addition, the change introduced type errors where I use specific units such as radiance and spectral flux density. I assume the source of error is one of the power index of the dimensions cancelling out and going to zero. I'm trying to create a minimum working example. (Minimum not-working example, to be more precise.)
Anyway, my intuition is that scalar product doesn't change the direction of vector; 2 *| x
is equivalent to x |+| x
and qSum [x, x]
. If the latter two does not normalize the dimension, my thinking is that the former don't, too.
Firstly, many thanks for making this library available!
I'm having a bit of a conceptual issue/question that I haven't been able to resolve. I've managed to use this module quite successfully where I have all my quantities/units/dimensions and combinations thereof specified at compile time.
I am now trying to put together a little demonstration application which would accept Unit strings at runtime from a user and, provided the strings are parsed succesfully, perform conversions between them.
On a successful parse of an input string I have some data UnitExp pre unit
. But I am not sure how to proceed from there. For instance a test function to try and make a quantity (say I have parsed "m/s" and lifted UnitExp out of the Either monad):
liftUnitExp :: (Show unit) => (UnitExp pre unit) -> Double -> IO () --Qu dim lcsu n
liftUnitExp (UnitExp pre unit) x = print (x % unit)
Gives me a compilation error:
• Could not deduce (UnitFactor
(LookupList (DimFactorsOf (DimOfUnit unit)) 'DefaultLCSU))
arising from a use of ‘%’
This seems to be because I'm unsure of what the proper way of associating an appropriate LCSU is with my parsed UnitExp Data. Essentially the problem seems to be that I can't see a way of making a "runtime-type equivalent" to MkQu_ULN Unit LCSU Double
, something like MkQuRunTime_ULN UnitExp LCSU Double
.
So I suppose the question is: am I trying to do something too far counter to what the units/units-parser is designed for? Or is there some pattern I can apply so that I can get past the type checker and have the UnitExp -> Quantity evaluated at runtime.
Cheers.
I'm trying to understand how to get numbers into units. Currently, it seems like I must use Double
for the quantities. But if I have some seconds in sec :: Int
, and I want a Time
, I'm doing fromIntegral sec % Second
. Is that to be expected, or is there a better/different way?
Consider a data type like this:
data Object lcsu vec = Object { m :: Mass lcsu (Scalar vec), v :: Velocity lcsu vec }
It is defined independently of the number of dimensions of its vectors.
Now, say you want to construct an Object
like this (expanded to show the type annotations):
ve :: Velocity SI (Double, Double)
ve = (1, 1) % Meter :/ Second
ma :: Mass SI Double
ma = 1 % Gram
object = Object ma ve
The compiler will complain to you that (shortened):
Couldn't match type ‘Qu
'['F Data.Dimensions.SI.Length One] lcsu0 (t0, t1)
:/ Second’
with ‘Qu
(DimFactorsOf Data.Dimensions.SI.Velocity)
(MkLCSU
'[(Data.Dimensions.SI.Length, Meter),
(Data.Dimensions.SI.Mass, Kilo :@ Gram),
(Data.Dimensions.SI.Time, Second),
(Data.Dimensions.SI.Current, Ampere),
(Data.Dimensions.SI.Temperature, Kelvin),
(Data.Dimensions.SI.AmountOfSubstance, Mole),
(Data.Dimensions.SI.LuminousIntensity, Lumen)])
(Double, Double)’
Expected type: Velocity SI (Double, Double)
Actual type: Qu
'['F Data.Dimensions.SI.Length One] lcsu0 (t0, t1)
:/ Second
In the expression: (1, 1) % Meter :/ Second
In an equation for ‘ve’: ve = (1, 1) % Meter :/ Second
The example solves this by creating the Vec2D
type family, but I'm wondering if there is any way to solve this without giving up the dimension polymorphism mentioned above.
Yesterday, I was able to locate the source of an error that have been haunting me for a while. First, please look at the following code. The function area1
is defined with correct unit, but is used with wrong unit in another function ratio
. The type error is correctly reporting the unit error in the body of ratio
.
https://github.com/goldfirere/units/blob/master/Tests/Compile/Error/Case1b.hs
However, if we make a slight modification to the above code, evil happens.
https://github.com/goldfirere/units/blob/master/Tests/Compile/Error/Case1a.hs
In Case1a.hs
, the function modelNorm
is defined with correct unit, but is used with wrong unit in another function. However, the error is reported at the point definition (which is nothing wrong) and not at the point of use. These kind of error message may cause the users to waste time looking hard for error in the functions, that contains no error (as I did.) Strangely enough, the error related to function area1
is also reported at wrong place in Case1a.hs
, even though the source code parts related to area1
is not modified at all.
The only thing I can see that connects the two parts is the QofU
type synonym. I can also reproduce the symptom using MkQu_ULN
, a type synonym that belongs to units
library.
https://github.com/goldfirere/units/blob/master/Tests/Compile/Error/Case1c.hs
I cannot figure out any further about this error. Is this a mis-design of the library (hard to believe,) or is it a GHC bug? The behavior reproduces on the latest ghc-7.8.3. Since this is beyond my understanding, Let me report and consult to upstream (first to @goldfirere and then maybe to the GHC team.)
Hi,
I was porting some old code of mine where I implemented some custom parsers - yes, I am aware of units-parser but it doesn't quite fit my needs - and in this code I was probably abusing the type system for something that is no longer allowed (in 7.10.1). I've converted the types to Units 2.3, but still:
instance ParseUnit Meter (Qu (DimFactorsOf (DimOfUnit Meter)) 'DefaultLCSU Double) where
parseUnit g = parseUnit' g
Still yields:
Illegal type synonym family application in instance: Qu ...
So, how can I declare class instances for quantities like (5 % Second)
, what should I use for the type?
Thanks
It is very easy to get dimension and unit declarations wrong, leading to terrible error messages. This feature request is to add a "lint" function, written in Template Haskell, to check unit and dimension definitions for sanity and report decent error messages.
Now that @goldfirere has implemented long-wanted qq-maker for creating unit expressions, I also want TH function for creating unit types.
However, my attempt to insert even basic Q Type
s have been failed, although the type signature appears to be nothing wrong for me.
I must confess that I have little experience writing Template Haskells, and I need help.
Data/Metrology/Parser.hs:77:9:
No instance for (Language.Haskell.TH.Syntax.Lift QuasiQuoter) ...
for 947e5d0
No instance for (Language.Haskell.TH.Syntax.Lift (String -> Q Type))
for 2fc598a
The unit of "Hertz" is usually written 1/s, but I'm not sure that's what people really mean. I think Hertz is more usefully defined as cycles/second, where 1 cycle is 360 degrees, 2pi radians, 2 semi-circles, etc. So I think the right dimension for Hertz is angle/time. Is that sensible and consistent with common usage?
Right now, ##
is monomorphic while #
is polymorphic. This goes against the general design of making the monomorphic things more accessible than polymorphic things, as non-experts will want the monomorphic ones.
(Suggested by João Cristóvão)
Building library for units-2.4.1.1..
[ 1 of 19] Compiling Data.Metrology.Set ( Data/Metrology/Set.hs, dist/build/Data/Metrology/Set.o )
[ 2 of 19] Compiling Data.Metrology.Z ( Data/Metrology/Z.hs, dist/build/Data/Metrology/Z.o )
Data/Metrology/Z.hs:70:3: error:
Illegal visible type application ‘@PSym0’
Perhaps you intended to use TypeApplications
|
70 | $(singletons [d| data Z = Zero | S Z | P Z deriving Eq |])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Data/Metrology/Z.hs:70:3: error:
Illegal visible type application ‘@SSym0’
Perhaps you intended to use TypeApplications
|
70 | $(singletons [d| data Z = Zero | S Z | P Z deriving Eq |])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At the end of the Travel.hs test, there is a comment with expected output. Currently, on my machine, the output differs somewhat.
Expected output:
46.5 mi
40.0 mi/gal
7.29 lb/gal
8.474625 lb
74.834496 km
14.160249 km/l
0.7273698 kg/l
3.844025 kg
My current output:
46.5 mi
39.999996 mi/gal
7.29 lb/gal
8.474626 lb
74.834496 km
14.160248 km/l
0.7273698 kg/l
3.8440251 kg
Most alarming to me is the second line, with significant rounding error. In examining what's going on, though, this seems somewhat reasonable. The input is 40 mi/gal. Yet, according to the LCSU, this quantity is stored in terms of yards/(cubic yard) -- which is much larger. The precision-saving idea around using Rational
doesn't really help here, because we are storing the large number between conversions. So, I think that the observed behavior can be considered correct, but I want a second opinion.
Should the units package define some set of useful physical constants? I tend to think "no", but I want to document the thought. It could even be argued that the SI
definitions should be in a separate package.
Thanks to the initial joyful development, now the core features are settled but the modules are bit disorganized (as often happens). For example classes Dimension
and Unit
are both defined in Data.Dimensions.Units
. The data type Dim
defined in Data.Dimensions.Dim
expresses quantity rather than dimension. Modules Data.Quantity.*
are obsolete and are to be removed.
To save the future users and developers of units
from confusion, I think I'll take a night and reorganize the modules. This kind of renamings come with pain and are always controversial, but if done at earlier stage less is the pain.
I'll first wait for any opinions.
Building units-1.0.0...
Preprocessing library units-1.0.0...
[ 1 of 12] Compiling Data.Dimensions.TypePrelude ( src/Data/Dimensions/TypePrelude.hs, dist/build/Data/Dimensions/TypePrelude.o )
src/Data/Dimensions/TypePrelude.hs:25:63:
parse error on input `where'
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.6.3
I can't seem to find what version of GHC introduced these closed type families.
Instead of writing, say, 5 % (Meter :/ Second)
, it would be nicer to write, say, 5 [unit| m/s |]
or something similar.
Looking at the Haddock documentation, I get pointed to the README here:
http://www.cis.upenn.edu/~eir/packages/units/README.html
But that README seems to be for units-1.*
. So I have no idea if it still applies to units-2.*
.
Might be worth updating the README? Or at least the Haddock docs that point to it?
The Haddock docs for the Z
module include some singletons-related gubbins. An export list would squelch this.
All the schemes described above, use a simplified representation of physical types, and do not incorporate ‘kind-of-quantity’ (KOQ), which is another attribute [7] of a physical type, at a higher abstraction than dimension. Thus, two quantities may have the same dimensions, but can be different kinds of quantities. Familiar examples are energy and torque, both of dimension ML2T-2, and heat capacity and entropy, both of dimension ML2T-2Θ-1.
https://www.researchgate.net/publication/326550053_Physical-type_correctness_in_scientific_Python
Is it possible with this units
package to e.g. prevent addition of a value of type torque to a value of type energy?
I'm just leaving this here as I ponder it. Food for thought, so to speak.
I wanted to use Time
instead of NominalDiffTime
, so I came up with:
import qualified Data.Time.Clock as C
addUTCTime :: Time -> UTCTime -> UTCTime
addUTCTime d = C.addUTCTime (realToFrac (d # Second))
Again, this has to do with conversions, as mentioned in #19. I'm not sure if there's a better way to do this.
As I use units
and units-defs
more, I realize that unit-polymorphism, as you call it, is useful but a lot of other libraries rely on specific units (e.g. microseconds for threadDelay
as @jcristovao has wrapped in unbounded-delays-units
and seconds with picosecond resolution for NominalDiffTime
). Perhaps when you revisit this library, you can think about conversion between quantities of a particular unit and unit-polymorphic quantities. Or maybe we just use fromIntegral
and realToFrac
.
nushio3 says:
I'm thinking of introducing a type constructor
data q :# u
instance Show (q:#u) where show (q:#u) = showIn q u
which is essentially a tuple of a quantity and a unit, designating that we want to associate the quantity with the specified unit.
(Copied from #12, where this is less relevant.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.