Can "overloading" via FlexibleInstances return different types, or match on typeclasses?
我很好奇在 Haskell 的类型类中可以通过"FlexibleInstances"实现什么样的"重载"。
作为一个简单的测试,这里是一个 AdjusterType 数据类型的例子。它定义了一个
1 2 3 4 5 6 7 8 9 10 11 12 13 | {-# 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 |
效果符合预期:
1 2 3 4 5 |
是否可以让
概括
1 2 3 4 5 |
这会产生一个错误,指出"n"是一个与整数不匹配的刚性类型变量:
1 2 3 4 5 6 7 | Couldn't match expected type a€?na€? with actual type a€?Integera€? a€?na€? 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 a€?(+)a€?, namely a€?aa€? In the expression: a + 20 |
这里期望 Integer 不匹配的类型是什么类型...或者实际上没有类型可以工作,这只是一个奇怪的错误消息? (n 是小写,所以大概它知道它不是数据类型)
实例规范中的类型约束似乎也不参与匹配解析:
1 2 3 4 5 | 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 |
所以它们的行为就像是重复的,就好像它们都是
有没有办法为类型类提供像上面这样的重载行为,或者必须始终提供给特定实例?
Is it possible to make the Integer version of adjust return an Integer, and the Double version return a Double?
你可以让
里面的内容
1 2 3 4 | {-# LANGUAGE MultiParamTypeClasses #-} class Adjustable f a where adjust :: f a -> a |
那么实例应该是:
1 2 3 4 5 |
还有来自 ghci 的一些结果:
1 2 3 4 5 6 7 8 |
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?
Is there any way to provide an overloaded behavior like above to a type class, or must it always be to a specific instance?
如果您希望函数对每种不同的类型表现不同,是的,您必须为每种类型添加一个实例。不过,您可以通过限制类的类型参数来添加一些默认行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {-# 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 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | {-# LANGUAGE TypeFamilies, FlexibleInstances #-} class Adjustable a where type Elem a :: * adjust :: a -> Elem a data AdjusterType a = Adjuster a deriving (Show) instance Adjustable (AdjusterType Integer) where type Elem (AdjusterType Integer) = Integer adjust (Adjuster a) = a + 20 instance Adjustable (AdjusterType Double) where type Elem (AdjusterType Double) = Double adjust (Adjuster a) = a + 0.04 main = do let x = Adjuster 1 :: AdjusterType Integer y = Adjuster 1 :: AdjusterType Double print $ adjust x print $ adjust y |
编译,输出为:
1 2 | 21 1.04 |