Others have suggested modifying the data structure, here is a different approach using pattern synonyms:
{-# Language GADTs, PatternSynonyms, ViewPatterns, TypeOperators #-}
import Data.Kind
data MyURL a where
GalleryURL :: URL -> MyURL URL
PictureURL :: URL -> MyURL URL
url :: MyURL a -> URL
url (GalleryURL u) = u -- Proof that: URL ~ a
url (PictureURL u) = u -- Proof that: URL ~ a
-- Works on ‘MyURL a’ for any ‘a’
pattern Url :: URL -> MyURL a
pattern Url u <- (url -> u)
Should another constructor be added that does not contain a URL
we must add a failure case for url
data MyURL a where
GalleryURL :: URL -> MyURL URL
PictureURL :: URL -> MyURL URL
I :: Int -> MyURL Int
url :: MyURL a -> Maybe URL
url (GalleryURL u) = Just u -- Proof that: URL ~ a
url (PictureURL u) = Just u -- Proof that: URL ~ a
url (I i) = Nothing -- Proof that: Int ~ a
pattern Url :: URL -> MyURL a
pattern Url u <- (url -> Just u)
showMyURL :: MyURL a -> String
showMyURL (Url u) = show u
showMyURL (I i) = show i -- Proof that: Int ~ a
Yet! — Let's say that we want an evaluation function that returns an a
when given MyURL a
— this works as intended
eval :: MyURL a -> a
eval (GalleryURL url) = url -- Proof that: URL ~ a
eval (PictureURL url) = url -- Proof that: URL ~ a
eval (I int) = int -- Proof that: Int ~ a
but our new Url
pattern synonym fails!
eval :: MyURL a -> a
eval (Url url) = url
We get no new information about a
when we pattern match on the pattern synonym
pattern Url :: URL -> MyURL a
the connection between a
and URL
has been severed. We import Data.Type.Equality and add a proof Refl :: a :~: URL
that a
equals URL
:
-- Gets ‘URL’ from a ‘MyURL URL’
--
-- ‘Refl’ is our proof that the input is ‘MyURL URL’
url :: MyURL a -> Maybe (a :~: URL, a)
url (GalleryURL url) = Just (Refl, url)
url (PictureURL url) = Just (Refl, url)
url (I int) = Nothing
Then we say that Url _
provides a proof that a ~ URL
when matched against,
pattern Url :: () => a ~ URL => a -> MyURL a
pattern Url url <- (Url -> (Refl, url))
and the URL
can again be retrieved with a single pattern
eval :: MyURL a -> a
eval (Url url) = url -- Proof that: URL ~ a
eval (I int) = int -- Proof that: Int ~ a