使用功能依赖关联的参数限制

时间:2012-09-13 05:14:15

标签: haskell types functional-dependencies

对于给定类型“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

由于

2 个答案:

答案 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特别棒。

我曾经有过工作习惯:

  1. 介绍一个多参数类型类(类型在各处悬挂,所以......)
  2. 介绍功能依赖(应该整理它,但最后我需要......)
  3. 添加FlexibleInstances(警报响铃开始振铃。编译器默认情况下关闭它的原因是......)
  4. 添加UndecidableInstances(GHC告诉您自己是独立的,因为它不相信您可以设置它所面临的挑战。)
  5. 一切都爆炸了。不知何故重构。
  6. 然后我发现了type families的乐趣(类型的函数式编程(万岁) - 多参数类型类(有点像)类型的逻辑编程)。我的工作流程更改为:

    1. 引入包含关联类型的类型类,即替换

      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
      
    2. 紧张地添加FlexibleInstances。什么都没有出错。

    3. 有时通过使用(MyJustWorksClass j,j~a)=>而不是(MyJustWorksClass a)=>(Show t,t ~ Thing a,...)=>而不是(Show (Thing a),...) =>等约束来帮助ghc解决问题。 (~实质上意味着'与'的类型相同')
    4. 紧张地添加FlexibleContexts。什么都没有出错。
    5. 一切正常。
    6. “根本没有出错”的原因是ghc 使用我的类型函数Thing a计算类型Thang,而不是尝试推断它只使用一堆断言,那里有一个功能,它应该能够解决它。

      试一试!在阅读Fun with Type Functions之前阅读manual