2016-03-02 56 views
3

我很好奇通过“FlexibleInstances”可以在Haskell的类型类中完成什么样的“重载”。可以通过FlexibleInstances“重载”返回不同类型,还是匹配类型类?

作为一个简单的测试,这里是AdjusterType数据类型的一个例子。它定义了一个adjust操作,将根据是否包含一个整数或双不同的量添加到它的价值:

{-# 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 

是否有可能使Integer版本的adjust返回一个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”是一个刚性的类型的变量不匹配整数:

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 

是期待它什么类型这里整数不匹配......或将没有类型的实际工作,它只是一个奇怪的错误消息?(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))。约束仅适用于解决方案完成后。

有没有什么办法可以像上面那样向类型类提供重载行为,或者它是否必须总是针对特定的实例?

+0

你想使用该[类型系列(https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/type-families.html)。 – Bakuriu

回答

3

是否有可能使Integer版本的调整返回一个Integer,而Double版本会返回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 

它在这里期待什么类型的整数不匹配...或者没有类型实际工作,它只是一个奇怪的错误信息?

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 
+0

感谢您的彻底回应!关于“类型家庭”如何影响这方面的内容,关于Bakuriu的答案是什么? – HostileFork

+0

@HostileFork类型系列是至关重要的,甚至在您希望返回类型“adjust”遵循一些规则而不是简单地匹配输入类型时,甚至是必需的。 'adjust :: AdjusterType Int - > Double'和'adjust :: AdjusterType Double - > Int',在这种情况下,type family通常是唯一的解决方案或最好的类型推断,代码样式等等。对于您当前的用例,它是一个可能的(也许更可扩展的)解决方案等等。 – zakyggaps

2

使用类型家庭提供的解决方案,特别是相关的数据类型,如下:

{-# 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.04 
+0

有趣,谢谢你的例子。好的,问的问题...这似乎能够解决*“是否有可能使整数版本的调整返回一个整数,并且双重版本返回一个双重?”*,这将完成,没有参数化可调整与另一种类型。这是需要的,还是有一些像'adjust(Adjuster a)= a +(20 :: Integer)'这可以做同样的技巧?这不起作用(抱怨Double,即使它是整数情况)......这是为了解决这个问题的目的吗? – HostileFork