Вы действительно не можете избежать здесь класса типов или эквивалента. unwrap
, поскольку вы написали его тип, не имеет возможности узнать , какой тег он ищет, потому что типы стираются. В идиоматическом подходе c используется одноэлементный шаблон.
data SValType v where
SText :: SValType 'Text
SBool :: SValType 'Bool
class KnownValType (v :: ValType) where
knownValType :: SValType v
instance KnownValType 'Text where
knownValType = SText
instance KnownValType 'Bool where
knownValType = SBool
unwrap :: forall tag. KnownValType tag => SomeValue -> Maybe (Value tag)
unwrap (SomeValue v) = case knownValType @tag of
SText
| T _ <- v -> Just v
| otherwise -> Nothing
SBool
| B _ <- v -> Just v
| otherwise -> Nothing
В отличие от класса IsType
вашего собственного ответа, KnownValType
позволяет вам получать информацию о типе, а также тег значения из сопоставления с образцом. . Таким образом, вы можете использовать его в более общем плане для работы с этими типами.
В тех случаях, когда вашего typeOf
достаточно, мы можем без проблем написать его:
typeOf :: KnownValType a => Proxy a -> ValType
typeOf (_ :: Proxy a) = case knownValType @a of
SBool -> Bool
SText -> Text