haskell之"overloading"能否通过 FlexibleInstances 返回不同的类型,或者匹配类型类

Demo 阅读:101 2025-06-02 22:19:02 评论:0

我很好奇在 Haskell 的类型类中可以通过“FlexibleInstances”完成什么样的“重载”。

作为一个简单的测试,这里是一个 AdjusterType 数据类型的例子。它定义了一个 adjust 操作,该操作将根据它包含的是 Integer 还是 Double 来为其值添加不同的数量:

{-# LANGUAGE FlexibleInstances #-} 
 
class Adjustable a where 
    adjust :: a -> Double 
 
data AdjusterType a = Adjuster a 
    deriving Show 
 
instance Adjustable (AdjusterType Integer) where 
    adjust (Adjuster a) = fromIntegral (a + 20) 
 
instance Adjustable (AdjusterType Double) where 
    adjust (Adjuster a) = a + 0.04 

正如预期的那样有效:

Prelude> adjust (Adjuster (1000 :: Integer)) 
1020.0 
 
Prelude> adjust (Adjuster (3 :: Double)) 
3.04 

是否可以让 adjust 的 Integer 版本返回 Integer,而 Double 版本返回 Double?

泛化 adjust 的签名并删除整数情况下的 fromIntegral 不起作用:

class Adjustable a where 
    adjust :: Num n => a -> n 
 
instance Adjustable (AdjusterType Integer) where 
    adjust (Adjuster a) = a + 20 

这会产生一个错误,指出“n”是一个与 Integer 不匹配的固定类型变量:

Couldn't match expected type ‘n’ with actual type ‘Integer’ 
    ‘n’ is a rigid type variable bound by 
       the type signature for adjust :: Num n => AdjusterType Integer -> n 
Relevant bindings include 
    adjust :: AdjusterType Integer -> n 
In the first argument of ‘(+)’, namely ‘a’ 
In the expression: a + 20 

Integer 不匹配时它期望的是什么类型...或者没有类型实际工作并且它只是一个奇怪的错误消息? (n 是小写的,所以大概是知道它不是数据类型)

实例规范中的类型约束似乎也没有参与匹配解析:

instance Integral i => Adjustable (AdjusterType i) where 
    adjust (Adjuster a) = fromIntegral (a + 20) 
 
instance RealFloat r => Adjustable (AdjusterType r) where 
    adjust (Adjuster a) = a + 0.04 

所以它们就像重复项一样,就好像它们都是Adjustable (AdjusterType x))。约束仅在解析完成后适用。

有没有办法为类型类提供像上面那样的重载行为,或者它必须始终针对特定实例?

请您参考如下方法:

Is it possible to make the Integer version of adjust return an Integer, and the Double version return a Double?

您可以使 Adjustable 类型类接受两个类型参数而不是一个,这样它就会知道 AdjusterType 中的内容:

{-# LANGUAGE MultiParamTypeClasses #-} 
 
class Adjustable f a where 
    adjust :: f a -> a 

那么实例应该是:

instance Adjustable AdjusterType Int where 
    adjust (Adjuster a) = a + 20 
 
instance Adjustable AdjusterType Double where 
    adjust (Adjuster a) = a + 0.04 

以及 ghci 的一些结果:

> :set +t 
 
> adjust (Adjuster (100 :: Int)) 
< 120 
< it :: Int 
> adjust (Adjuster (100 :: Double)) 
< 100.04 
< it :: Double 

What type was it expecting here that Integer isn't matching...or would no type actually work and it's just a weird error message?

adjust 的返回类型是 forall n 类型。 Num n => n,一种具有单一约束 Num 的多态类型,因此返回具体类型的函数不会进行类型检查。用 fromIntegral 包装你的函数将解决问题,因为 fromIntegral::(Integral a, Num b) => a -> b

Is there any way to provide an overloaded behavior like above to a type class, or must it always be to a specific instance?

如果您希望该函数对每种不同的类型都有不同的行为,是的,您必须为每种类型添加一个实例。不过,您可以通过限制类的类型参数来添加一些默认行为:

{-# LANGUAGE DeriveFunctor         #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
 
class Extract f where 
  extract :: f a -> a 
 
class (Extract f, Functor f, Num a) => Adjustable f a where 
  adjust :: f a -> a 
  adjust = extract . fmap (+20) 
 
data AdjusterType a = Adjuster a 
  deriving (Functor) 
 
instance Extract AdjusterType where 
  extract (Adjuster a) = a 
 
instance Adjustable AdjusterType Int where 
-- don't have to write any code here 


标签:程序员
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号