告诉Haskell编译器两种类型兼容

oxf4rvwz  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(149)

下面是Haskell函数:

apply_to_f :: (a -> a) -> ((a -> a) -> b) -> ((b -> c) -> c)
apply_to_f f = \ g -> \ h -> h (g f)

字符串
(This来自教会数字的“pred”函数)。
我想告诉编译器一个约束:(apply_to_f f)可以与自身组合。
换句话说,(apply_to_f f)接受一个(a -> a) -> a类型的函数,它也返回一个(a -> a) -> a类型的函数。
那么,是否有可能告诉编译器,它需要将类型(a -> a) -> b(b -> c) -> c统一为apply_to_f类型,或者如果我想写下函数的精确类型,我必须自己弄清楚这一点吗?
我最初的猜测是,我也许能够为apply_to_f编写一个模糊类型,使用类型变量来指示此约束,但这似乎行不通。

atmip9wb

atmip9wb1#

有几种可能性。首先,你可以将额外的约束表示为类型相等:

apply_to_f :: ((a -> a) -> b) ~ ((b -> c) -> c)
  => (a -> a) -> ((a -> a) -> b) -> ((b -> c) -> c)

字符串
注意,结果类型签名 * 不 * 精确等价于:

apply_to_f :: (a -> a) -> ((a -> a) -> a) -> ((a -> a) -> a)


特别的,GHCi会为这两种类型的签名报告不同的类型,如果apply_to_f在"错误类型"使用,报告的错误也会不同。例如,下面的代码类型检查:

apply_to_f :: (a -> a) -> ((a -> a) -> b) -> ((b -> c) -> c)
apply_to_f f = \ g -> \ h -> h (g f)

foo = apply_to_f id id


如果您使用直接类型签名限制apply_to_f的类型:

apply_to_f :: (a -> a) -> ((a -> a) -> a) -> ((a -> a) -> a)


所产生的类型错误将与foo的定义中的第二个id相关联,并将伴随着对第二个id的预期版本实际类型的清楚解释:

Unify.hs:13:21-22: error:
    • Couldn't match type ‘a’ with ‘a -> a’
      Expected: (a -> a) -> a
        Actual: (a -> a) -> a -> a
      ‘a’ is a rigid type variable bound by
        the inferred type of foo :: (a -> a) -> a
        at Unify.hs:13:1-22
    • In the second argument of ‘apply_to_f’, namely ‘id’
      In the expression: apply_to_f id id
      In an equation for ‘foo’: foo = apply_to_f id id
    • Relevant bindings include
        foo :: (a -> a) -> a (bound at Unify.hs:13:1)
   |
13 | foo = apply_to_f id id
   |                     ^^


而如果你使用一个约束:

apply_to_f :: ((a -> a) -> b) ~ ((b -> c) -> c)
  => (a -> a) -> ((a -> a) -> b) -> ((b -> c) -> c)


那么类型错误将与foo的定义中的apply_to_f的使用相关联,并且将更模糊地与"foo的推断类型中的一些问题"相关联:

Unify.hs:12:7-16: error:
    • Couldn't match type ‘a’ with ‘a -> a’
        arising from a use of ‘apply_to_f’
      ‘a’ is a rigid type variable bound by
        the inferred type of foo :: ((a -> a) -> a -> a) -> a -> a
        at Unify.hs:12:1-22
    • In the expression: apply_to_f id id
      In an equation for ‘foo’: foo = apply_to_f id id
    • Relevant bindings include
        foo :: ((a -> a) -> a -> a) -> a -> a (bound at Unify.hs:12:1)
   |
12 | foo = apply_to_f id id
   |       ^^^^^^^^^^


尽管如此,我认为这两个签名之间的差异在大多数情况下可能是表面上的,它们在实际应用中应该表现得差不多。
其次,您可以利用类型检查器来计算apply_to_f的所需类型,方法是强制类型检查器对需要约束的表达式进行类型检查。例如,定义:

apply_to_f = general_apply_to_f
  where general_apply_to_f :: (a -> a) -> ((a -> a) -> b) -> ((b -> c) -> c)
        general_apply_to_f f = \ g -> \ h -> h (g f)

        _ = apply_to_f undefined . apply_to_f undefined


将导致apply_to_f的类型被正确推断为:

λ> :t apply_to_f
apply_to_f :: (a -> a) -> ((a -> a) -> a) -> (a -> a) -> a

相关问题