【发布时间】:2012-09-06 03:49:27
【问题描述】:
下面的函数 f,对于给定的类型“a”,接受类型为“c”的参数。对于不同类型的“a”,“c”以不同的方式进行限制。具体来说,当“a”是任何 Integral 类型时,应该允许“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
谢谢
【问题讨论】:
-
当你说
class AllowedParamType a c | a -> c时,你说给定任何类型a作为第一个参数,最多有一个类型c可以用作第二个参数。但是你说当第一个类型是Integral类型时,anyReal类型可以用作第二个参数。理想情况下,GHC 会给您一条指出这一点的错误消息。 -
这不是真的。这只是意味着 a 唯一确定 b 因此允许上述类型的实例。
-
Satvik 是对的,fundep 的独特性不应该是导致代码无法编译的原因。但是,dave4420 也有正确的想法:从长远来看,fundep 是行不通的,因为我不想将整个程序的 Int 参数类型限制为一种 Real。
-
@dave4420:但实例不是这么说的。它实际上说“当第一种类型是任何类型时,第二种类型也是任何类型,哦顺便确保他们有
Integral和@分别为 987654329@ 个实例”。选择实例时会忽略Integral约束。 -
'r' 并不总是一个 Real 类型,它还可以包含我们声明的一些特定的 Data 值(都将是 Num),尽管 f 的具体实现取决于具体的 Num类型。为了清楚 'a' 和 'c' 之间的关系,我想为 (Num a) 做一些 Foo 的实例。对于每个实例,c 有一个(唯一)可能的类型(如 Float),或整个类型类(如 Real)。 'c' 不必与 'a' 相关,在某些情况下它们可以是“独立”类型(对于我们的用户定义类型)。
标签: haskell types functional-dependencies