可能“超载”通过FlexibleInstances返回不同的类型,或者匹配类型类? [英] Can "overloading" via FlexibleInstances return different types, or match on typeclasses?

查看:109
本文介绍了可能“超载”通过FlexibleInstances返回不同的类型,或者匹配类型类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



作为一个简单的测试,这里是一个例子,我们可以通过使用FlexibleInstances AdjusterType数据类型。它定义了一个 adjust 操作,根据它是否包含Integer或Double来为其值添加一个不同的值:

  { - #LANGUAGE FlexibleInstances# - } 

class可调a其中
adjust :: a - > Double

数据AdjusterType a =调整者a
派生显示

实例可调整(AdjusterType整数)其中
调整(Adjuster a)= fromIntegral(a + 20)

实例可调(AdjusterType Double)其中
adjust(Adjuster a)= a + 0.04

多少可以按预期工作:

  Prelude> adjust(Adjuster(1000 :: Integer))
1020.0

Prelude> adjust(Adjuster(3 :: Double))
3.04

有可能使整数版本的 adjust 返回一个Integer,而Double版本返回Double?



一般化调整的签名并从整数大小写除去 fromIntegral 不起作用:

  class可调a其中
adjust :: Num n => a - > n

实例可调(AdjusterType整数)其中
adjust(Adjuster a)= a + 20

这会产生一个错误,指出n是一个刚性类型变量,它与Integer不匹配:

 无法与实际类型'Integer'匹配的预期类型'n'
'n'是一个刚性类型变量,由
绑定调整:: Num n =>的类型签名。 AdjusterType整数 - > n
相关绑定包括
adjust :: AdjusterType Integer - > n'
在'(+)'的第一个参数中,即'a'
在表达式中:a + 20

它在这里期望什么类型的Integer不匹配...或者没有实际的类型工作,这只是一个奇怪的错误信息? n是小写字母,所以推测它知道它不是数据类型)



实例规范中的类型约束也不会参与匹配的分辨率:

 实例Integral i => Adjustable(AdjusterType i)其中
adjust(Adjuster a)= fromIntegral(a + 20)

实例RealFloat r => Adjustable(AdjusterType r)其中
adjust(Adjuster a)= a + 0.04

就像重复一样,就好像它们都是 Adjustable(AdjusterType x))。约束只适用于解析完成后。



是否有任何方法可以为类型类提供像上面这样的重载行为,或者它总是一个特定的实例?

解决方案


是否有可能使整数版本的调整返回一个Integer,Double版本返回Double?


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

 { - #LANGUAGE MultiParamTypeClasses# - } 

class可调fa,其中
adjust :: fa - > a

然后这些实例应该是:

<$
adjust(Adjuster a)= a + 20

实例Adjustable AdjusterType Double其中
adjust(Adjuster a)p $ p $ = a + 0.04

以及ghci的一些结果:

 > :set + t 

> adjust(Adjuster(100 :: Int))
< 120
< it :: Int
>调整(Adjuster(100 :: Double))
< 100.04
< it :: Double




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

返回类型 adjust 的类型是 forall n。 Num n => n ,一个带有单个约束 Num 的多态类型,所以返回具体类型的函数不会输入check。使用 fromIntegral 包装你的函数将解决 fromIntegral ::(Integral a,Num b)=> a - > b 。


有没有什么办法可以像上面那样为类型类提供重载行为,总是对特定的实例吗?

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

  { - #LANGUAGE DeriveFunctor# - } 
{ - #LANGUAGE MultiParamTypeClasses# - }

class Extract f where
extract :: fa - > a

类(Extract f,Functor f,Num a)=>可调整的f a其中
adjust :: f a - >
adjust = extract。 fmap(+20)

数据AdjusterType a =调整器a
派生(Functor)

实例Extract AdjusterType其中
extract(Adjuster a)= a

实例Adjustable AdjusterType Int其中
- 不必在此处写任何代码


I'm curious about what kind of "overloading" can be accomplished in Haskell's type classes via "FlexibleInstances".

As a simple test, here is a case of an AdjusterType datatype. It defines an adjust operation that will add a different amount to its value based on whether it contains an Integer or a 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

That much works as expected:

Prelude> adjust (Adjuster (1000 :: Integer))
1020.0

Prelude> adjust (Adjuster (3 :: Double))
3.04

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

Generalizing the signature of adjust and removing the fromIntegral on the integer case doesn't work:

class Adjustable a where
    adjust :: Num n => a -> n

instance Adjustable (AdjusterType Integer) where
    adjust (Adjuster a) = a + 20

This produces an error saying that "n" is a rigid type variable that doesn't match 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

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? (n is lowercase, so presumably it knows it's not a datatype)

Type constraints in the instance specifications also don't appear to participate in the matching resolution:

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

So these act like duplicates, as if they were both Adjustable (AdjusterType x)). The constraint only applies after the resolution is done.

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

解决方案

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

You can make the Adjustable type class accept two type parameters instead of one, so it will know what's inside the AdjusterType:

{-# LANGUAGE MultiParamTypeClasses #-}

class Adjustable f a where
    adjust :: f a -> a

Then the instances should be:

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

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

And some results from 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?

The return type of adjust is of type forall n . Num n => n, a polymorphic type with a single constraint Num, so your function returning a concrete type won't type check. Wrap your function with fromIntegral will solve the problem since 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?

If you expect the function to behave differently for every distinct type, yes you have to add an instance for each. You may add some default behavior though, by restricting the type parameters of the class:

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

这篇关于可能“超载”通过FlexibleInstances返回不同的类型,或者匹配类型类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆