haskell - 在这种情况下如何防止类型范围?
问题描述
instance IsString Escaped where fromString a = Escaped a
instance ConvertibleStrings URI.URI String where convertString = show
data Escaped = forall a. ConvertibleStrings a String => Escaped a
instance Show Escaped where show (Escaped a) = cs a
class Escaping a where
esc1 :: a -> Escaped
esc2 :: a -> Escaped
instance ConvertibleStrings a String => Escaping a where
esc1 a = Escaped $ escape1 $ cs a
esc2 a = Escaped $ escape2 $ cs a
现在,OverloadedStrings
启用扩展后,如果esc1
我esc2
使用[sec1 ("a"::String), ...]
. 如何防止它,所以只有一个[sec1 "a", ...]
?可能吗?我尝试了 DS 扩展,但在这种情况下没有帮助。
解决方案
我试过 DS 扩展
我假设您所说的“DS 扩展”是指DefaultSignatures
。这使您可以在实例未实现该方法时为类型类方法指定默认签名。它通常用于Generic
派生代码,而不是这样的情况。
如果您想指定字符串文字的默认类型,您可能需要使用default
声明,它指定在解决模棱两可的类型时要搜索的类型。但是启用时的默认 default
声明OverloadedStrings
是default (Integer, Double, String)
,因此String
如果可能的话,应该已经选择了。如果您仍然有歧义,则有不同的原因,很可能是由ConvertibleString
orEscaping
类引起的,它们没有内置的默认规则IsString
。
但是,这里有一个更简单的解决方案,可以从一开始就避免歧义。这个类型:
data Escaped
= forall a. ConvertibleStrings a String
=> Escaped a
或与GADTs
语法等效的:
data Escaped where
Escaped :: (ConvertibleStrings a String) => a -> Escaped
是所谓的存在主义反模式的一个例子。它说Escaped
包含某个隐藏类型的值,a
该类型的实例为ConvertibleStrings a String
. 但是该类的唯一方法是convertString
,这里有a -> String
hidden的类型a
。这意味着您唯一可以对 an 做的事情Escaped
就是将其用作String
! String
因此,仅存储 a并使用 a要简单得多,newtype
因为只有一个字段:
newtype Escaped = Escaped String
由于懒惰,String
除非使用它,否则不会对其进行评估,因此您无需担心。事实上,将 typeclass 约束存储在一个存在中比这稍微贵一些。
此外,这种模式通常会导致歧义:
class Escaping a where
esc1 :: a -> Escaped
esc2 :: a -> Escaped
instance ConvertibleStrings a String => Escaping a where
esc1 a = Escaped $ escape1 $ cs a
esc2 a = Escaped $ escape2 $ cs a
它说所有类型都是 的实例Escaping
,因为“实例头”是Escaping a
。实例上的任何约束,如ConvertibleStrings a String
这里,仅在使用实例时在事后检查。
您可以尝试通过使用类型注释 ( cs :: a -> String
) 或TypeApplications
( cs @a @String
) 约束类型来解决这些歧义,以使类型参数具体化,但这里更简单的解决方案是只制作需要约束的普通函数esc1
,而不是类型类方法:esc2
ConvertibleStrings
esc1 :: (ConvertibleStrings a String) => a -> Escaped
esc1 a = Escaped $ escape1 $ cs a
esc2 :: (ConvertibleStrings a String) => a -> Escaped
esc2 a = Escaped $ escape2 $ cs a
(同样,您可以对 a 做的唯一事情forall a. (ConvertibleStrings a String) => a
是将其转换为 a String
,因此这也等同于String -> Escaped
,尽管在此函数中进行转换可能比在每个调用站点更方便。)
一般而言,具有单个参数的类型类表示一组类型,具有多个参数的类型类ConvertibleStrings
表示类型之间的关系或函数。ConvertibleStrings
已经代表了你想要的类型关系;esc1
并且esc2
可以简单地使用这种关系。大多数时候,您可以(并且应该!)仅使用数据类型和函数来解决问题,而无需创建自己的类型类。
推荐阅读
- kubernetes - 如何在 GKE 中将私有 DNS 区域添加到 kube-dns
- intellij-idea - 您如何在 IntelliJ 中查看、设置和访问变量,以便将其从一个插件传递到系统属性?
- pandas - 按行索引将 1 列数据帧行拆分为新列
- git - 具有不同提交历史的公共/私人 git 存储库
- git - 如何将版本化文件夹更改为 git 版本和分支?
- java - 使用 Node.js 打印 RTF/HTML 文件
- c++ - 在 Qt/c++ 项目中使用 MVC 时,如何通知单独窗口中的视图?
- c++ - OpenCV - FAST+BRIEF:如何使用 DrawMatchesFlags::DRAW_RICH_KEYPOINTS 绘制关键点?
- asp.net-mvc - HTTP Get 上查询字符串中的可读数据
- java - 在用户计算机上的 sqlite DB 上使用 jdbc 设置相对路径