{- |
Copyright : (c) Henning Thielemann 2008
License : GPL
Maintainer : numericprelude@henning-thielemann.de
Stability : provisional
Portability : portable
We already have the dynamically checked physical units
provided by "Number.Physical"
and the statically checked ones of the @dimensional@ package of Buckwalter,
which require multi-parameter type classes with functional dependencies.
Here we provide a poor man's approach:
The units are presented by type terms.
There is no canonical form and thus the type checker
can not automatically check for equal units.
However, if two unit terms represent the same unit,
then you can tell the type checker to rewrite one into the other.
You can add more dimensions by introducing more types of class 'C'.
This approach is not entirely safe
because you can write your own flawed rewrite rules.
It is however more safe than with no units at all.
-}moduleAlgebra.DimensionTermwhereimportPreludehiding(recip){- Haddock does not like 'where' clauses before empty declarations -}classShowa=>Ca-- wherenoValue::Ca=>anoValue=letx=error("there is no value of type "++showx)inx{- * Type constructors -}dataScalar=ScalardataMulab=MuldataRecipa=ReciptypeSqra=MulaaappPrec::IntappPrec=10instanceShowScalarwhereshow_="scalar"instance(Showa,Showb)=>Show(Mulab)whereshowsPrecpx=letdisect::Mulab->(a,b)disect_=undefined(y,z)=disectxinshowParen(p>=appPrec)(showString"mul ".showsPrecappPrecy.showString" ".showsPrecappPrecz)instance(Showa)=>Show(Recipa)whereshowsPrecpx=letdisect::Recipa->adisect_=undefinedinshowParen(p>=appPrec)(showString"recip ".showsPrecappPrec(disectx))instanceCScalar-- whereinstance(Ca,Cb)=>C(Mulab)-- whereinstance(Ca)=>C(Recipa)-- wherescalar::Scalarscalar=noValuemul::(Ca,Cb)=>a->b->Mulabmul__=noValuerecip::(Ca)=>a->Reciparecip_=noValueinfixl7%*%infixl7%/%(%*%)::(Ca,Cb)=>a->b->Mulab(%*%)=mul(%/%)::(Ca,Cb)=>a->b->Mula(Recipb)(%/%)xy=mulx(recipy){- * Rewrites -}applyLeftMul::(Cu0,Cu1,Cv)=>(u0->u1)->Mulu0v->Mulu1vapplyLeftMul__=noValueapplyRightMul::(Cu0,Cu1,Cv)=>(u0->u1)->Mulvu0->Mulvu1applyRightMul__=noValueapplyRecip::(Cu0,Cu1)=>(u0->u1)->Recipu0->Recipu1applyRecip__=noValuecommute::(Cu0,Cu1)=>Mulu0u1->Mulu1u0commute_=noValueassociateLeft::(Cu0,Cu1,Cu2)=>Mulu0(Mulu1u2)->Mul(Mulu0u1)u2associateLeft_=noValueassociateRight::(Cu0,Cu1,Cu2)=>Mul(Mulu0u1)u2->Mulu0(Mulu1u2)associateRight_=noValuerecipMul::(Cu0,Cu1)=>Recip(Mulu0u1)->Mul(Recipu0)(Recipu1)recipMul_=noValuemulRecip::(Cu0,Cu1)=>Mul(Recipu0)(Recipu1)->Recip(Mulu0u1)mulRecip_=noValueidentityLeft::Cu=>MulScalaru->uidentityLeft_=noValueidentityRight::Cu=>MuluScalar->uidentityRight_=noValuecancelLeft::Cu=>Mul(Recipu)u->ScalarcancelLeft_=noValuecancelRight::Cu=>Mulu(Recipu)->ScalarcancelRight_=noValueinvertRecip::Cu=>Recip(Recipu)->uinvertRecip_=noValuedoubleRecip::Cu=>u->Recip(Recipu)doubleRecip_=noValuerecipScalar::RecipScalar->ScalarrecipScalar_=noValue{- * Example dimensions -}{- ** Scalar -}{- |
This class allows defining instances that are exclusively for 'Scalar' dimension.
You won't want to define instances by yourself.
-}classCdim=>IsScalardimwheretoScalar::dim->ScalarinstanceIsScalarScalarwheretoScalar=id{- ** Basis dimensions -}dataLength=LengthdataTime=TimedataMass=MassdataCharge=ChargedataAngle=AngledataTemperature=TemperaturedataInformation=Informationlength::Lengthlength=noValuetime::Timetime=noValuemass::Massmass=noValuecharge::Chargecharge=noValueangle::Angleangle=noValuetemperature::Temperaturetemperature=noValueinformation::Informationinformation=noValueinstanceShowLengthwhereshow_="length"instanceShowTimewhereshow_="time"instanceShowMasswhereshow_="mass"instanceShowChargewhereshow_="charge"instanceShowAnglewhereshow_="angle"instanceShowTemperaturewhereshow_="temperature"instanceShowInformationwhereshow_="information"instanceCLength-- whereinstanceCTime-- whereinstanceCMass-- whereinstanceCCharge-- whereinstanceCAngle-- whereinstanceCTemperature-- whereinstanceCInformation-- where{- ** Derived dimensions -}typeFrequency=RecipTimefrequency::Frequencyfrequency=noValuedataVoltage=VoltagetypeVoltageAnalytical=Mul(Mul(SqrLength)Mass)(Recip(Mul(SqrTime)Charge))voltage::Voltagevoltage=noValueinstanceShowVoltagewhereshow_="voltage"instanceCVoltage-- whereunpackVoltage::Voltage->VoltageAnalyticalunpackVoltage_=noValuepackVoltage::VoltageAnalytical->VoltagepackVoltage_=noValue