Reputation:
I have a type which looks something like this.
data State = Unsanitized | Sanitized
type family BarTy (s :: State) :: Type where
BarTy 'Unsanitized = String
BarTy 'Sanitized = Int
data Foo (a :: State) = Foo (Bar a)
data Bar (a :: State) = Bar (BarTy a)
I use the type parameter to be able to change types of certain fields. This is useful for instance when evolving a data structure through a validation pipeline.
Now I want to write a class that can extract something out of this data structure but it doesn't matter which State
it is. Full example:
data State = Unsanitized | Sanitized
type family BarTy (s :: State) :: Type where
BarTy 'Unsanitized = String
BarTy 'Sanitized = Int
data Foo (a :: State) = Foo (Bar a)
data Bar (a :: State) = Bar (BarTy a)
class Extractor a where
extract :: a -> (BarTy b)
instance Extractor (Foo a) where
extract (Foo x) = extract x
instance Extractor (Bar a) where
extract (Bar x) = x
This obviously doesn't work since the b
type variable is always unknown in the instances and thus a type error occurs. I can make another type family that extracts the State
type from a given type but this is really clunky for larger types.
data State = Unsanitized | Sanitized
type family BarTy (s :: State) :: Type where
BarTy 'Unsanitized = String
BarTy 'Sanitized = Int
type family ExtractState (t :: Type) :: State where
ExtractState (Foo s) = s
ExtractState (Bar s) = s
ExtractState _ = TypeError ('Text "ExtractState: Passed type is not valid.")
data Foo (a :: State) = Foo (Bar a)
data Bar (a :: State) = Bar (BarTy a)
class Extractor a where
extract :: a -> (BarTy (ExtractState a))
instance Extractor (Foo a) where
extract (Foo x) = extract x
instance Extractor (Bar a) where
extract (Bar x) = x
bar1 :: Bar 'Unsanitized
bar1 = Bar "Test"
foo1 = Foo bar1
bar2 :: Bar 'Sanitized
bar2 = Bar 1337
foo2 = Foo bar2
a = extract bar1
b = extract foo1
c = extract bar2
d = extract foo2
What I would ideally like to do is pattern match in the class declaration but I don't know how to do this or if it is even possible:
data State = Unsanitized | Sanitized
type family BarTy (s :: State) :: Type where
BarTy 'Unsanitized = String
BarTy 'Sanitized = Int
data Foo (a :: State) = Foo (Bar a)
data Bar (a :: State) = Bar (BarTy a)
class Extractor (t a) where
extract :: (t a) -> (BarTy a)
instance Extractor (Foo a) where
extract (Foo x) = extract x
instance Extractor (Bar a) where
extract (Bar x) = x
Can Haskell do something like this? Or is there another way to achieve what I want?
Any help is appreciated.
Upvotes: 2
Views: 68
Reputation: 8477
Remember that you can partially apply types when defining a typeclass:
class Extractor t where
extract :: t a -> BarTy a
(The same technique is used in the definitions for Functor
, Applicative
, Monad
and in general just about any typeclass meant for use with higher-kinded types.)
Upvotes: 5