Validation 类型级别的验证
假设我想构造满足某些不变量的子类型,而无需借助LiquidHaskell等外部工具(理想情况下,即使没有类型类,我也希望这样做)。最优雅的方式是什么?到目前为止,我尝试了以下方法:Validation 类型级别的验证,validation,haskell,type-families,Validation,Haskell,Type Families,假设我想构造满足某些不变量的子类型,而无需借助LiquidHaskell等外部工具(理想情况下,即使没有类型类,我也希望这样做)。最优雅的方式是什么?到目前为止,我尝试了以下方法: class Validated a where type Underlying a validate :: Underlying a -> Bool construct :: Underlying a -> a use :: a -> Underlying a makeValida
class Validated a where
type Underlying a
validate :: Underlying a -> Bool
construct :: Underlying a -> a
use :: a -> Underlying a
makeValidated :: Validated a => Underlying a -> Maybe a
makeValidated u = if validate u
then Just (construct u)
else Nothing
newtype Name = Name String
instance Validated Name where
type Underlying Name = String
validate str = and [ isUppercase (str !! 0 )
, all isLetter str ]
construct = Name
use (Name str) = str
我假设如果我不从模块中导出“Name”构造函数,我将有一个有效的解决方案,因为构造该类型元素的唯一方法是通过makeValidated函数
然而,编译器抱怨如下:
Could not deduce (Underlying a0 ~ Underlying a)
from the context (Validated a)
bound by the type signature for
makeValidated :: Validated a => Underlying a -> Maybe a
at validated.hs:11:18-55
NB: `Underlying' is a type function, and may not be injective
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
In the first argument of `validate', namely `u'
In the expression: validate u
In the expression:
if validate u then Just (construct u) else Nothing
如何修复它?编写的
验证
功能在当前GHC中不可用。查看其类型签名:
validate :: Validated a => Underlying a -> Bool
您可能会合理地认为,给定a下面的类型的值,可以确定要使用哪个验证的
实例,即a
实例。但这是一个错误:因为底层
不是内射的,所以可能存在b
和c
类型,它们的底层b~底层c
;因此,无论是b
还是c
都不能作为使用哪个实例的标准选择。也就是说,对于F(底层a)~a
始终为真的类型,没有良好的映射F
另一种方法是使用数据族而不是类型族
class Validated a where
data Underlying a
validate :: Underlying a -> Bool
instance Validated Name where
data Underlying Name = Underlying String
validate (Underlying name) = ...
底层
是一个类型函数,它可能不是内射函数。即:
instance Validate T1 where
type Underlying T1 = Int
validate = ... -- code A
instance Validate T2 where
type Underlying T2 = Int
validate = ... -- code B
现在考虑一下
validate (42 :: Int)
这该怎么办?它应该调用codeA
还是B
?由于底层T1=底层T2=Int
,因此无法判断
不可能毫不含糊地调用validate
。为了避免这种情况,一种可能的修复方法是在验证函数中添加一个“代理”参数:
data Proxy a = Proxy
class Validate a where
validate :: Proxy a -> Underlying a -> Bool
现在您可以使用:
validate Proxy (42 :: Int) -- still ambiguous!
validate (Proxy :: Proxy T1) (42 :: Int) -- Now OK!
validate (Proxy :: Proxy T2) (42 :: Int) -- Now OK!
我试着修改我的代码如下,但我得到相同的错误消息。我做错了什么<代码>数据代理a=Proxy`
类验证a其中
类型基础a
验证::代理a->基础a->Bool
构造::基础a->a使用::a->基础a`makeValidated::验证a=>基础a->可能是a
makeValidated u=如果validate(Proxy::Proxy a)u
则只需(构造u)
其他什么都不需要
@NioBium您需要使用makeValidated::for all a。已验证a=>…
并启用ScopedTypeVariables
语言扩展。可能更好的选择是从类中删除validate,构造,然后在类中添加makeValidated
。