1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DataKinds #-}
4 {-# LANGUAGE ExistentialQuantification #-}
5 {-# LANGUAGE FlexibleContexts #-}
6 {-# LANGUAGE FlexibleInstances #-}
8 {-# LANGUAGE MultiParamTypeClasses #-}
9 {-# LANGUAGE OverloadedStrings #-}
10 {-# LANGUAGE ScopedTypeVariables #-}
11 {-# LANGUAGE TypeApplications #-}
12 {-# LANGUAGE TypeFamilies #-}
13 {-# LANGUAGE TypeOperators #-}
14 module Data.Locale where
16 import Data.Eq (Eq(..))
17 import Data.Function (($), (.))
18 import Data.Functor ((<$>))
19 import Data.Map.Strict (Map)
20 import Data.Maybe (Maybe(..), isJust)
21 import Data.Ord (Ord(..), Ordering(..))
22 import Data.Semigroup (Semigroup(..))
23 import Data.Text (Text)
24 import Data.Tuple (swap)
25 import Data.Type.Equality ((:~:)(..))
26 import Prelude (error, max)
27 import Text.Show (Show(..))
28 import qualified Data.Char as Char
29 import qualified Data.Map.Strict as Map
30 import qualified Data.Text as Text
37 type family Index xs x where
38 Index (x ': xs) x = Zero
39 Index (not_x ': xs) x = Succ (Index xs x)
42 data Locale (ls::[*]) (l:: *) where
43 LocaleZ :: FullLocale l -> Locale (l ': ls) l
44 LocaleS :: Locale ls l -> Locale (not_l ': ls) l
47 instance ( Show (FullLocale l)
49 ) => Show (Locale (l ': ls) l) where
52 s0:s1:s@('_':_) -> Char.toLower s0:Char.toLower s1:s
54 show (LocaleS l) = show l
62 eqLocale (LocaleZ x) (LocaleZ y) =
63 if x == y then Just Refl else Nothing
64 eqLocale (LocaleS x) (LocaleS y) = eqLocale x y
65 eqLocale _x _y = Nothing
73 compareLocale (LocaleZ x) (LocaleZ y) = compare x y
74 compareLocale (LocaleS x) (LocaleS y) = compareLocale x y
75 compareLocale LocaleZ{} LocaleS{} = LT
76 compareLocale LocaleS{} LocaleZ{} = GT
78 -- ** Type 'FullLocale'
79 data family FullLocale (l:: *) :: *
86 ) => LocaleIn (Locale ls l)
88 instance Eq (LocaleIn ls) where
89 LocaleIn x == LocaleIn y = isJust (eqLocale x y)
90 instance Ord (LocaleIn ls) where
91 compare (LocaleIn x) (LocaleIn y) = compareLocale x y
92 instance Locales ls => Show (LocaleIn ls) where
93 show = Text.unpack . (textLocales @ls Map.!)
95 -- ** Class 'LocaleInj'
97 = LocaleInjP (Index ls l) ls l
98 localeInj :: forall l ls. LocaleInj ls l => FullLocale l -> Locale ls l
99 localeInj = localeInjP @(Index ls l)
101 -- *** Class 'LocaleInjP'
102 class LocaleInjP p ls l where
103 localeInjP :: FullLocale l -> Locale ls l
104 instance LocaleInjP Zero (l ': ls) l where
106 instance LocaleInjP p ls l =>
107 LocaleInjP (Succ p) (not_t ': ls) l where
108 localeInjP = LocaleS . localeInjP @p
110 -- * Class 'Localize'
111 class Localize ls msg a where
112 localize :: LocaleIn ls -> a -> msg
113 instance LocalizeIn l msg a => Localize '[l] msg a where
114 localize (LocaleIn li) =
116 LocaleZ l -> localizeIn @l l
117 LocaleS{} -> error "localize: impossible locale"
118 instance ( LocalizeIn l msg a
119 , Localize (l1 ': ls) msg a
120 ) => Localize (l ': l1 ': ls) msg a where
121 localize (LocaleIn (LocaleZ l)) = localizeIn @l l
122 localize (LocaleIn (LocaleS l)) = localize (LocaleIn l)
124 -- ** Class 'LocalizeIn'
125 class LocalizeIn l msg a where
126 localizeIn :: FullLocale l -> a -> msg
129 class Locales ls where
130 locales :: Map Text (LocaleIn ls)
132 textLocales :: Locales ls => Map (LocaleIn ls) Text
133 textLocales = Map.fromListWith max $ swap <$> Map.toList locales
135 instance Locales '[] where
137 instance (LocalesFor l, Locales ls, Ord (FullLocale l)) => Locales (l ': ls) where
140 (\k _n _o -> error $ "locales: duplicate locale: "<>Text.unpack k)
141 (LocaleIn . localeInj <$> localesFor @l)
142 ((\(LocaleIn l) -> LocaleIn $ LocaleS l) <$> locales @ls)
144 -- ** Class 'LocalesFor'
145 class LocalesFor l where
146 localesFor :: Map Text (FullLocale l)
150 data instance FullLocale FR
156 deriving (Eq,Ord,Show)
157 instance LocalesFor FR where
158 localesFor = Map.fromList
166 fr_FR :: LocaleInj ls FR => Locale ls FR
167 fr_FR = localeInj FR_FR
171 data instance FullLocale EN
190 deriving (Eq,Ord,Show)
191 instance LocalesFor EN where
192 localesFor = Map.fromList
213 en_US :: LocaleInj ls EN => Locale ls EN
214 en_US = localeInj EN_US