我很好奇在Haskell的类型类中可以通过"FlexibleInstances“完成什么样的”重载“。
作为一个简单的测试,这里有一个AdjusterType数据类型的例子。它定义了一个adjust操作,该操作将根据它是包含整数还是包含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.04adjust 的的整数版本有可能返回整数,而双版本返回双?吗?
概括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不是matching...or,它实际上不会工作,而它只是一条奇怪的错误消息? (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))一样。该约束仅在决议完成后才适用。
是否有向类型类提供像上面这样的重载行为的方法,或者必须始终将其提供给特定实例?
发布于 2016-03-02 12:01:38
是否可以使整定版本的调整返回一个整数,而双版本返回一个双?
您可以让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在这里,期望Integer不是matching...or的类型是什么类型的,它实际上不会工作,它只是一条奇怪的错误消息?
adjust的返回类型是forall n . Num n => n类型,具有单个约束Num的多态类型,因此返回具体类型的函数不会进行类型检查。使用fromIntegral包装您的函数将解决这个问题,因为fromIntegral :: (Integral a, Num b) => a -> b。
是否有任何方法向类型类提供像上面这样的重载行为,或者必须始终将其提供给特定实例?
如果您期望该函数对每种不同的类型都有不同的行为,则必须为每个类型添加一个实例。但是,您可以通过限制类的类型参数来添加一些默认行为:
{-# 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发布于 2016-03-02 20:46:40
使用类型族(特别是关联数据类型)的解决方案如下:
{-# 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它编译并输出如下:
21
1.04https://stackoverflow.com/questions/35745191
复制相似问题