对于给定类型“a”,下面的函数f采用类型为“c”的参数。对于不同类型的'a','c'以不同方式受到限制。具体地说,当'a'是任何积分类型时,'c'应该被允许为任何'Real'类型。当'a'是Float时,'c'只能是Float。
一次尝试是:
{-# 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
出于某种原因,GHC 7.4.1抱怨它“无法推断使用fIntegral引起的(Real c)”。在我看来,功能依赖应该允许这种推论。在实例中,a与i统一,因此通过函数依赖,d应该与c统一,在实例中声明为'Real'。我在这里缺少什么?
除了功能依赖,这种方法是否足以表达以强制执行上述限制,还是有更好的方法?我们只为'a'使用了几个不同的值,因此会出现如下情况:
instance (Integral i, Real c) => AllowedParamType i c
instance AllowedParamType Float Float
由于
答案 0 :(得分:3)
一种可能更好的方法是使用约束种类和类型族(GHC扩展,需要GHC 7.4,我认为)。这允许您将约束指定为类实例的一部分。
{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleInstances, UndecidableInstances #-}
import GHC.Exts (Constraint)
class Foo a where
type ParamConstraint a b :: Constraint
f :: ParamConstraint a b => b -> a
instance Integral i => Foo i where
type ParamConstraint i b = Real b
f = fIntegral
编辑:经过进一步的实验,有一些细微之处意味着这不能按预期工作,具体而言,type ParamConstraint i b = Real b
过于笼统。我现在不知道解决方案(或者如果存在)。
答案 1 :(得分:0)
这是一个解决更普遍问题的建议,而不是你的具体问题(我还需要更多细节 - 我保证稍后再检查)。我正在写它,以防其他人正在寻找解决类似问题的方法,在我发现SO之前,我当然是在过去。当它帮助你尝试一种全新的方法时,SO特别棒。
我曾经有过工作习惯:
然后我发现了type families的乐趣(类型的函数式编程(万岁) - 多参数类型类(有点像)类型的逻辑编程)。我的工作流程更改为:
引入包含关联类型的类型类,即替换
class MyProblematicClass a b | a -> b where
thing :: a -> b
thang :: b -> a -> b
与
class MyJustWorksClass a where
type Thing a :: * -- Thing a is a type (*), not a type constructor (* -> *)
thing :: a -> Thing a
thang :: Thing a -> a -> Thing a
紧张地添加FlexibleInstances。什么都没有出错。
(MyJustWorksClass j,j~a)=>
而不是(MyJustWorksClass a)=>
或(Show t,t ~ Thing a,...)=>
而不是(Show (Thing a),...) =>
等约束来帮助ghc解决问题。 (~
实质上意味着'与'的类型相同')“根本没有出错”的原因是ghc 使用我的类型函数Thing a
计算类型Thang
,而不是尝试推断它只使用一堆断言,那里有一个功能,它应该能够解决它。
试一试!在阅读Fun with Type Functions之前阅读manual !