繁体   English   中英

Haskell在类型签名中使用类型类

[英]Haskell using typeclasses inside type signatures

假设一个简单的类型类约束签名:

f :: (Eq a, Num b) => a -> b
f str = 4

我想知道为什么这些都不起作用

f :: (Eq a) -> (Num b)
f str = 4

f :: Eq -> Num
f str = 4

我知道类型类具有* -> Constraint类型,而类型签名仅接受*

但是我的问题是为什么会有这个限制? 为什么不能像类型一样使用类型类? 允许使用类似类型的类型类的优点和缺点是什么?

(如果我们忽略未装箱的类型)只有一种类型实际上具有任何值的类型: * 所有其他种类均不包含此类,而仅包含“类型级实体”。 编译器可以使用它们来确定如何处理实际类型及其周围的值,但是在运行时永远不可能拥有* -> Constraint类的“类型”值。

*那种对价值类型仅仅是一个游戏规则。 拥有一个好东西,出于同样的原因,拥有一个强大的静态类型系统也是个好主意,该系统可以在运行时防止不必要的转换。 瓦特? 或者,从字面上看,出于同样的原因,无论您遇到什么特殊情况,无论该功能多么吸引人,您都不能仅仅让国王跳过您的棋子。

如果某些扩展确实允许从非*类型的类型(特别是* -> Constraint ,则我们需要一大堆非显而易见的定义来明确说明这些“类值”的实际含义用过的。 可能,它等于一个包含类的方法作为字典的记录类型。 但是,到底...规范将是一场噩梦。 而且以这种方式使语言本身复杂化绝对是不值得的,因为1. 1.使用类型类的标准方法至少对所有应用程序的95%都适用,并且2.当需要确实类型化的类时,您可以使用GADT,ConstraintKinds甚至是普通的手动定义的字典记录,都非常容易做到这一点。 所有这些都不需要像非*类型那样扭曲关于语言如何对待价值的基本观念。


无论如何...让我们探讨一下它可能如何工作。 有一两件事是肯定的:它不会让你写什么东西那样简单f str = 4

考虑

f1 :: forall a, b . Eq a -> Num b

两个Eq a, Num b :: Constraint ,所以我们会有类型Constraint值。 基本上,这将是给定实例的特定方法字典。 所以f1的实现必须看起来像

f1 (EqDict (d_eq :: a -> a -> Bool))
       = NumDict { dict_fromInteger = ??? :: Integer -> b
                 , dict_plus        = ??? :: b -> b -> b
                 , ...
                 , dict_signum      = ??? :: b -> b
                 }

显然,没有有意义的方法可以在结果中定义所有这些方法。 使用这样的“类功能”,您所能做的就是将“项目”从一个较强的类“投射”到一个较弱的类。 例如,您可以定义

monadApp :: forall m . Monad m -> Applicative m
monadApp (MonadDict {dict_return = d_ret, dict_bind = d_bd})
        = ApplicativeDict { dict_pure = d_ret
                          , dict_app = \fs vs -> d_bd fs (\f -> d_bd vs $ d_ret . f) }

实际上,那个特定的类会有些用,但这只是因为Monad仍然,但不会持续很长时间! )缺乏Applicative作为它应该具有的超类。 通常,不必过多地理由明确地“降级”任何类,因为超类关系(或tuple-ConstraintKinds)会自动做到这一点。

类型变量在类型签名中可能会出现多次:

f :: Eq a => a -> a -> a -> Bool

上面写为

f :: Eq a -> Eq a -> Eq a -> Bool

看起来不方便。 更糟的是,我们可能有一个以上的约束a

g :: (Show a, Eq a) => a -> Bool

我们将如何用替代记号来写呢?

g :: (Show & Eq) a -> Bool    -- ??

如您在上一个示例中建议的那样完全忘记了a ,会使签名不明确:请考虑

h1 :: (Eq a)       => a -> a -> a -> a -> Bool
h2 :: (Eq a, Eq b) => a -> a -> b -> b -> Bool

这些是完全不同的签名:您可以调用h2 1 2 [1] [2]但不能调用h1 1 2 [1] [2]因为后者要求四个参数具有相同的类型。 使用提议的约定后,它们将减少为相同的签名:

h12 :: Eq -> Eq -> Eq -> Eq -> Bool

通话h12 1 2 [1] [2]有效? 上述签名含糊不清。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM