首先,这是我的代码的最小例子:
{-# LANGUAGE GADTs #-}
-- package "url"
import Network.URL (exportURL, URL(..), URLType(..))
data MyURL a where
GalleryURL :: URL -> MyURL URL
PictureURL :: URL -> MyURL URL
url = URL { url_type = PathRelative,
url_path = "www.google.com",
url_params = []}
galleryURL = GalleryURL url
myExportURL :: MyURL a -> String
myExportURL (GalleryURL a) = exportURL a
myExportURL (PictureURL a) = exportURL a
main = print $ myExportURL galleryURL
我使用GADT来避免混合使用不同类型的URL。 myExportURL
函数对于所有类型的URL都是相同的。有没有办法使用这样的东西:
myExportURL (_ a) = exportURL a
而不是为GADT的每个子类型(正确的术语是什么?)重复它?
我也欢迎对我想解决的代码或问题的任何其他评论。
答案 0 :(得分:12)
正确的术语是“为每个构造函数”。
您的GADT看起来很可疑,因为所有构造函数都构造相同的MyURL URL
类型。如果这就是你想要的,那么你首先不需要GADT并且可以使用普通的ADT:
data MyURL = GalleryURL URL | PictureURL URL
要缩短myExportUrl
,有不同的选项。一种是重构类型:
data URLKind = GalleryURL | PictureURL
data MyURL = MyURL { myUrlKind :: URLKind, myExportURL :: URL }
这样你仍然可以使用“短”结构形式,例如: MyURL PictureURL foo
。此外,您还可以使用Haskell为您生成的myExportURL
函数。
只有在高级案例中才需要GADT,所以如果你的例子没有充分证明你为什么需要GADT,那就让我们吧吧。
答案 1 :(得分:8)
您的MyURL类型不会阻止您混合图库和图片网址。 GalleryURL和PictureURL都具有相同的类型MyURL URL
。尝试这样的事情:
data Gallery = Gallery
data Picture = Picture
data MyURL a where
MyURL :: a -> URL -> MyURL a
然后你可以像想象的那样编写代码的其余部分:
url = URL { url_type = PathRelative,
url_path = "www.google.com",
url_params = []}
galleryURL = MyURL Gallery url
myExportURL :: MyURL a -> String
myExportURL (MyURL _ a) = exportURL a
main = print $ myExportURL galleryURL
当所有构造函数适用于所有类型时,您不需要GADT,并且您实际上不需要Gallery和Picture类型的构造函数,因此您可以将这些部分编写为:
data Gallery -- requires the empty type extension
data Picture
data MyURL a = MyURL URL
galleryURL :: MyURL Gallery
galleryURL = MyURL url
myExportURL :: MyURL a -> String
myExportURL (MyURL a) = exportURL a
答案 2 :(得分:0)
其他人建议修改数据结构,这是使用模式同义词的不同方法:
{-# 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)
如果添加了另一个不包含URL
的构造函数,我们必须为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
不过! - 假设我们想要一个评估函数,在给定a
时返回MyURL a
- 这可以按预期工作
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
但我们的新Url
模式同义词失败了!
eval :: MyURL a -> a
eval (Url url) = url
当我们在模式同义词上模式匹配时,我们得到没有关于a
的新信息
pattern Url :: URL -> MyURL a
a
和URL
之间的联系已被切断。我们导入Data.Type.Equality并添加Refl :: a :~: URL
等于a
的证明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
然后我们说Url _
提供了a ~ URL
匹配时的证据,
pattern Url :: () => a ~ URL => a -> MyURL a
pattern Url url <- (Url -> (Refl, url))
并且可以使用单一模式再次检索URL
eval :: MyURL a -> a
eval (Url url) = url -- Proof that: URL ~ a
eval (I int) = int -- Proof that: Int ~ a