我正在尝试创建一个记录,其中一个字段具有未导出的类型,因为它使用的是智能构造函数.使用智能构造函数作为类型不起作用.
Not in scope: type variable `domain'
Run Code Online (Sandbox Code Playgroud)
也许有一个语言扩展允许我这样做,或类似的东西?
导出构造函数和智能构造函数将允许我解决此问题,但这反过来创建了创建智能构造函数不允许的值的可能性.
我现在拥有的(非工作)代码:
import Domain (domain) -- Domain is not exported, and domain is a smart constructor for Domain
data Rec = Rec
{ dint :: domain Int -- what do I do here? I want it to be `Domain Int` but `Domain` isn't exported.
...
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是类型构造函数的概念和数据构造函数之间的混淆.为简洁起见,我将通过一个例子说明不同之处.
data Foo a = Bar [a]
Run Code Online (Sandbox Code Playgroud)
在上面的表达式中,Foo是类型构造函数,Bar是数据构造函数.关键区别在于它Foo是Haskell类型空间Bar中的值,并且是其数据空间中的值.类型空间中的值不能用于数据空间,反之亦然.例如,编译器会在以下表达式中出错.
someVariable :: Bar Int
someVariable = Foo [15]
Run Code Online (Sandbox Code Playgroud)
但是,下一个表达式完全有效.
someVariable :: Foo Int
someVariable = Bar [15]
Run Code Online (Sandbox Code Playgroud)
此外,所有类型构造函数必须以大写字母开头.任何以小写字母开头的类型都将被视为类型变量,而不是类型构造函数(a我们在上面的定义中就是一个例子).
智能构造函数的引入为这个问题增加了另一层,但要理解的关键是智能构造函数是数据构造函数,而不是类型构造函数.在您的定义中Rec,您尝试domain在dint字段的类型声明中使用智能构造函数.但是,因为domain数据构造函数不是类型构造函数,并且它是小写的,所以Haskell编译器试图将其解释domain为类型变量的名称.因为你从来没有指定的命名变量domain在你们的definiton Rec类型,编译器产生错误.
您实际上不需要导出数据构造函数Domain来解决问题,只需要输入类型本身.这可以通过以下方式完成.
module Domain (
Domain(), domain,
...
) where
Run Code Online (Sandbox Code Playgroud)
包含Domain()在导出定义中告诉Haskell导出Domain类型构造函数,但不导出任何数据构造函数.这样可以使用安全构造函数保留所需的安全性,并允许您正确定义类型.您现在可以在定义中使用新导出的类型Rec.
import Domain (Domain(), domain)
data Rec = Rec
{ dint :: Domain Int
...
}
Run Code Online (Sandbox Code Playgroud)
有关更多信息,我强烈建议您阅读有关构造函数和智能构造函数的HaskellWiki文章.