首页 > 解决方案 > 在 Haskell 中,如何将约束附加到参数新类型,以便它们自动应用于使用它的任何类实例?

问题描述

假设我有一个这样定义的参数类型:

newtype FancyComplex a b = FancyComplex (a, b)

我打算永远不要将此新类型用于除数字参数之外的任何其他参数。我的意思是,对于我可能做的任何实现,我都知道参数a并且b将始终是Num.

我在这个问题中读到你可以这样做: Can a typeclass constraint be used in a newtype definition?

{-# LANGUAGE RankNTypes #-}
newtype (Num a, Num b) => FancyComplex a b = FancyComplex (a, b)

然而,这还不够。如果我写任何这样的课程:

class StupidClass x where add :: x -> x -> x

那我应该可以写了

instance StupidClass (FancyComplex a b) where
    add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')

但是没有 GHC 会告诉我说我没有执行该Num要求。所以我每次都被迫这样做:

instance (Num a, Num b) => StupidClass (FancyComplex a b) where
    add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')

在 newtype 定义中编写约束所做的所有事情都是迫使我每次都明确地编写约束。好的,如果我忘记了,这仍然很有用。但我当然希望不必每次都重写约束。

如何自动和隐式地从 newtype 定义中继承约束?这可能吗?如果没有,有理由不这样做吗?

目前我的弱解决方法是定义一个类型别名type FancyComplexReqs a b = (Num a, Num b)

谢谢

标签: haskellconstraintstypeclassnewtype

解决方案


在 GADT 中编码约束,如下所示:

{-# LANGUAGE GADTs #-}

data FancyComplex a b where
  FancyComplex :: (Num a, Num b) => a -> b -> FancyComplex a b

class StupidClass x where add :: x -> x -> x

instance StupidClass (FancyComplex a b) where
    add (FancyComplex a b) (FancyComplex a' b') = FancyComplex (a+a') (b+b')

你必须切换到datafromnewtype因为约束变成了字典,它确实具有运行时表示。但是,通过这样做,我们可以摆脱您的元组,从而节省data成本。


推荐阅读