使用函数依赖关联的参数限制 [英] Associated Parameter Restriction using Functional Dependency

查看:124
本文介绍了使用函数依赖关联的参数限制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的函数f,对于给定的类型'a',采用类型'c'的参数。对于不同类型的'a','c'以不同的方式受到限制。具体来说,当'a'是任何Integral类型时,'c'应该被允许为任何'Real'类型。当'a'是浮动时,'c'只能是浮动。



一次尝试是:

<$ p $ { - #LANGUAGE
MultiParamTypeClasses,
FlexibleInstances,
FunctionalDependencies,
UndecidableInstances# - }

class AllowedParamType ac | a - > c

class Foo a where
f ::(AllowedParamType a c)=> c - > a

fIntegral ::(Integral a,Real c)=> c - > a
fIntegral =错误执行消失

实例(Integral i,AllowedParamType i d,Real d)=> Foo i其中
f = fIntegral

由于某些原因,GHC 7.4.1抱怨它无法推断(实际c)由于使用fIntegral而产生。在我看来,功能依赖应该允许这种推论。在这个例子中,a与i是统一的,所以通过函数依赖,d应该与c一致,在实例中它被声明为'Real'。我在这里错过了什么?



抛开功能依赖关系,这种方法是否足以表达上述限制,还是有更好的方法?我们只处理'a'的一些不同的值,所以会有这样的情况:

  instance(Integral i, Real c)=> AllowedParamType ic 
实例AllowedParamType Float Float

谢谢

让我们走完整个猪圈,摆脱
源和目标类型之间的任何关系,而不是存在一个实例:

  { - #LANGUAGE OverlappingInstances,FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses# - } 

class Foo ab where f :: a - > b

现在我们可以用 f $ b $ pre $ instance Foo Int Int其中f =(+1)$ b ((7 :: Int) - )
实例Foo Integer Int其中f = fromInteger。(^(2 :: Integer))
实例Foo整数Integer Integer其中f = toInteger。其中f =(* 100)
实例Foo Char Char其中f = id
实例Foo Char字符串其中f =(:[]) - 需要TypeSynonymInstances
实例(Foo ab,Functor f )=> Foo(fa)(fb)其中f = fmap f - 需要FlexibleInstances
实例Foo Float Int其中f = round
实例Foo Integer Char其中fn = head $ show n

这确实意味着很多显式类型注释以避免没有实例用于... 不明确的类型错误消息。
例如,您不能执行 main = print(f 6),但您可以执行 main = print(f(6 :: Int):: Int)



您可以使用标准类型列出所有想要
这可能导致很多重复,我们可以点亮蓝色的触摸纸,然后做:

 实例Integral i => Foo Double i其中f = round  - 需要FlexibleInstances 
实例Real r => Foo Integer r其中f = fromInteger - 需要FlexibleInstances

请注意: mean嘿,如果你有一个整型 i
你可以有一个实例 Foo Double i 免费使用这个方便的函数,
表示:每个您有 类型类型 i ,它绝对是一个实例
Foo Double i 。顺便说一句,我使用 round ,因此,除非你的类型 i 积分
,否则我们会下跌去。例如,这对于 Foo Integer Char 实例来说是个大问题。



这很容易破坏其他实例,所以如果你现在输入 f(5 :: Integer):: Integer ,你可以得到

  Foo整数的重叠实例整数
使用`f'引起
匹配实例:
实例Foo整数整数
实例r => Foo整数r

您可以更改您的编译指示以包含OverlappingInstances:

  { - #LANGUAGE OverlappingInstances,FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses# - } 

所以现在 f(5 :: Integer):: Integer 返回500,很明显它使用更具体的 Foo整数整数实例。



我认为这种方法可能适用于您,手动定义多个实例,仔细考虑何时完全通配
使用标准类型类实例。 (或者,并非所有标准类型都是这样,我们都知道, notMany选择2 = notIntractablyMany ,因此您可以将它们全部列出。)


The function f below, for a given type 'a', takes a parameter of type 'c'. For different types 'a', 'c' is restricted in different ways. Concretely, when 'a' is any Integral type, 'c' should be allowed to be any 'Real' type. When 'a' is Float, 'c' can ONLY be Float.

One attempt is:

{-# LANGUAGE
MultiParamTypeClasses,
FlexibleInstances,
FunctionalDependencies,
UndecidableInstances #-}

class AllowedParamType a c | a -> c

class Foo a where
    f :: (AllowedParamType a c) => c -> a

fIntegral :: (Integral a, Real c) => c -> a
fIntegral = error "implementation elided"

instance (Integral i, AllowedParamType i d, Real d) => Foo i where
    f = fIntegral

For some reason, GHC 7.4.1 complains that it "could not deduce (Real c) arising from a use of fIntegral". It seems to me that the functional dependency should allow this deduction. In the instance, a is unified with i, so by the functional dependency, d should be unified with c, which in the instance is declared to be 'Real'. What am I missing here?

Functional dependencies aside, will this approach be expressive enough to enforce the restrictions above, or is there a better way? We are only working with a few different values for 'a', so there will be instances like:

instance (Integral i, Real c) => AllowedParamType i c
instance AllowedParamType Float Float

Thanks

解决方案

OK, this one's been nagging at me. given the wide variety of instances, let's go the whole hog and get rid of any relationship between the source and target type other than the presence of an instance:

{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-}

class Foo a b where f :: a -> b

Now we can match up pairs of types with an f between them however we like, for example:

instance Foo Int Int where f = (+1)
instance Foo Int Integer where f = toInteger.((7::Int) -)
instance Foo Integer Int where f = fromInteger.(^ (2::Integer))
instance Foo Integer Integer where f = (*100)
instance Foo Char Char where f = id
instance Foo Char String where f = (:[])  -- requires TypeSynonymInstances
instance (Foo a b,Functor f) => Foo (f a) (f b) where f = fmap f -- requires FlexibleInstances
instance Foo Float Int where f = round
instance Foo Integer Char where f n = head $ show n

This does mean a lot of explicit type annotation to avoid No instance for... and Ambiguous type error messages. For example, you can't do main = print (f 6), but you can do main = print (f (6::Int)::Int)

You could list all of the instances with the standard types that you want, which could lead to an awful lot of repetition, our you could light the blue touchpaper and do:

instance Integral i => Foo Double i where f = round -- requires FlexibleInstances
instance Real r => Foo Integer r where f = fromInteger -- requires FlexibleInstances

Beware: this does not mean "Hey, if you've got an integral type i, you can have an instance Foo Double i for free using this handy round function", it means: "every time you have any type i, it's definitely an instance Foo Double i. By the way, I'm using round for this, so unless your type i is Integral, we're going to fall out." That's a big issue for the Foo Integer Char instance, for example.

This can easily break your other instances, so if you now type f (5::Integer) :: Integer you get

Overlapping instances for Foo Integer Integer
  arising from a use of `f'
Matching instances:
  instance Foo Integer Integer
  instance Real r => Foo Integer r

You can change your pragmas to include OverlappingInstances:

{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-}

So now f (5::Integer) :: Integer returns 500, so clearly it's using the more specific Foo Integer Integer instance.

I think this sort of approach might work for you, defining many instances by hand, carefully considering when to go completely wild making instances out of standard type classes. (Alternatively, there aren't all that many standard types, and as we all know, notMany choose 2 = notIntractablyMany, so you could just list them all.)

这篇关于使用函数依赖关联的参数限制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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