haskell之"overloading"能否通过 FlexibleInstances 返回不同的类型,或者匹配类型类
我很好奇在 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.作者投稿可能会经我们编辑修改或补充。