Validation 类型级别的验证

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

假设我想构造满足某些不变量的子类型,而无需借助LiquidHaskell等外部工具(理想情况下,即使没有类型类,我也希望这样做)。最优雅的方式是什么?到目前为止,我尝试了以下方法:

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)
这该怎么办?它应该调用code
A
还是
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