adamgundry / uom-plugin

Units of measure as a GHC typechecker plugin
30 stars 5 forks source link

Haskell expressions as conversion factors #16

Open expipiplus1 opened 8 years ago

expipiplus1 commented 8 years ago

How realistic is this?

It would be nice to be able to define degrees as [u| deg = (pi / 180) rad |] and have that expression spliced into convert.

This would be particularly useful for my exact real package, where any constant ratio may not be precise enough.

adamgundry commented 8 years ago

There are two separate issues here.

The Template Haskell quasiquoter would need to parse and splice arbitrary Haskell expressions, which in principle is possible, but would require quite a bit of work. In the short term you can always expand the TH manually, which in this case would be something like

-- [u| rad |]
type instance MkUnit "rad" = Base "rad"
instance HasCanonicalBaseUnit "rad"

-- [u| deg = (pi / 180) rad |]
type instance MkUnit "deg" = Base "deg"
instance HasCanonicalBaseUnit "deg" where
  type CanonicalBaseUnit "deg" = "rad"
  conversionBase _ = (pi / 180) *: [u| 1 deg/rad |]

Unfortunately there is a bigger problem, which is that at the moment conversionBase is required to be Rational rather than an arbitrary numeric type. It might be possible to generalise this and keep convert, I forget the details...

expipiplus1 commented 8 years ago

It's probably a niche request. I'd be happy to look into generalizing conversionBase myself.

philderbeast commented 7 years ago

I need to convert between units of degrees and radians too. When radians are defined "to require explicit conversion", as they are in the Defs module with [u| rad 1 1 |], equivalent to declareConvertibleUnit "rad" 1 "1", I haven't found a way for degrees to be setup as a conversion from radians without getting an error ...

declareConvertibleUnit "rad" 1 "1"
declareConvertibleUnit "deg" (5030569068109113 % 288230376151711744) "rad"

Couldn't match type ‘One’ with ‘Base "rad"’
In the instance declaration for ‘HasCanonicalBaseUnit "deg"’

Dumping the template splices to show the equivalence of the two forms ...

[u| rad = 1 1 |]
stack build --ghc-options='-ddump-splices -ddump-to-file'
" rad = 1 1 "
======>
type instance MkUnit "rad" = Base "rad",
instance HasCanonicalBaseUnit "rad" where
  type CanonicalBaseUnit "rad" = One
  conversionBase _ = Data.UnitsOfMeasure.Internal.MkQuantity 1.0
declareConvertibleUnit "rad" 1 "1"
stack build --ghc-options='-ddump-splices -ddump-to-file'
declareConvertibleUnit "rad" 1 "1"
======>
type instance MkUnit "rad" = Base "rad"
instance HasCanonicalBaseUnit "rad" where
  type CanonicalBaseUnit "rad" = One
  conversionBase _ = Data.UnitsOfMeasure.Internal.MkQuantity 1.0

I could setup degrees like this as a dimensionless scaling not based on radians ...

[u| deg = (5030569068109113 % 288230376151711744) 1 |]

Here's my working.