{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.UnitsOfMeasure.Singleton
(
SUnit(..)
, forgetSUnit
, KnownUnit(..)
, unitVal
, testEquivalentSUnit
, SList(..)
, KnownList(..)
) where
import GHC.TypeLits
import Data.List (foldl')
import qualified Data.Map as Map
import Data.Type.Equality
import Unsafe.Coerce
import Data.UnitsOfMeasure.Internal
data SUnit (u :: UnitSyntax Symbol) where
SUnit :: SList xs -> SList ys -> SUnit (xs :/ ys)
data SList (xs :: [Symbol]) where
SNil :: SList '[]
SCons :: KnownSymbol x => proxy x -> SList xs -> SList (x ': xs)
instance TestEquality SUnit where
testEquality (SUnit xs ys) (SUnit xs' ys') = case (testEquality xs xs', testEquality ys ys') of
(Just Refl, Just Refl) -> Just Refl
_ -> Nothing
instance TestEquality SList where
testEquality SNil SNil = Just Refl
testEquality (SCons px xs) (SCons py ys)
| Just Refl <- testEqualitySymbol px py
, Just Refl <- testEquality xs ys = Just Refl
testEquality _ _ = Nothing
testEqualitySymbol :: forall proxy proxy' x y . (KnownSymbol x, KnownSymbol y)
=> proxy x -> proxy' y -> Maybe (x :~: y)
testEqualitySymbol px py
| symbolVal px == symbolVal py = Just (unsafeCoerce Refl)
| otherwise = Nothing
testEquivalentSUnit :: SUnit u -> SUnit v -> Maybe (Pack u :~: Pack v)
testEquivalentSUnit su sv
| normaliseUnitSyntax (forgetSUnit su) == normaliseUnitSyntax (forgetSUnit sv) = Just (unsafeCoerce Refl)
| otherwise = Nothing
normaliseUnitSyntax :: UnitSyntax String -> Map.Map String Integer
normaliseUnitSyntax (xs :/ ys) =
Map.filter (/= 0)
(foldl' (\ m x -> Map.insertWith (-) x 1 m)
(foldl' (\ m x -> Map.insertWith (+) x 1 m) Map.empty xs) ys)
forgetSUnit :: SUnit u -> UnitSyntax String
forgetSUnit (SUnit xs ys) = forgetSList xs :/ forgetSList ys
forgetSList :: SList xs -> [String]
forgetSList SNil = []
forgetSList (SCons px xs) = symbolVal px : forgetSList xs
class KnownUnit (u :: UnitSyntax Symbol) where
unitSing :: SUnit u
instance (KnownList xs, KnownList ys) => KnownUnit (xs :/ ys) where
unitSing = SUnit listSing listSing
class KnownList (xs :: [Symbol]) where
listSing :: SList xs
instance KnownList '[] where
listSing = SNil
instance (KnownSymbol x, KnownList xs) => KnownList (x ': xs) where
listSing = SCons (undefined :: proxy x) listSing
unitVal :: forall proxy u . KnownUnit u => proxy u -> UnitSyntax String
unitVal _ = forgetSUnit (unitSing :: SUnit u)