【问题标题】:Trying to abstract away typeclass, but a type variable escapes试图抽象出类型类,但类型变量转义
【发布时间】:2020-07-02 02:34:48
【问题描述】:

我有一些类和它们的实例。该示例显示了一些无意义的类。它们的确切性质并不重要。

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}


class Foo a where
   foo :: a -> Int
class Bar a where
   bar :: a -> String

instance Foo Int where
   foo x = x
instance Foo String where
   foo x = length x

instance Bar Int where
   bar x = show x
instance Bar String where
   bar x = x

好的,现在我想创建一些存在类型,将这些类隐藏在某些数据类型外观之后,因此我不必处理约束。 (我知道存在类型被认为是一种反模式,请不要向我解释)。

data TFoo = forall a. Foo a => TFoo a
instance Foo TFoo where
  foo (TFoo x) = foo x
data TBar = forall a. Bar a => TBar a
instance Bar TBar where
  bar (TBar x) = bar x

显然那里有一些样板。我想把它抽象出来。

{-# LANGUAGE ConstraintKinds #-}
data Obj cls = forall o. (cls o) => Obj o

所以我只有一个,而不是几个存在类型,由一个类型类参数化。到目前为止一切顺利。

现在如何对Obj a 执行操作?显而易见的尝试

op f (Obj a) = f a

因为类型变量可能逃逸而失败。

existential.hs:31:18: error:
    • Couldn't match expected type ‘o -> p1’ with actual type ‘p’
        because type variable ‘o’ would escape its scope
      This (rigid, skolem) type variable is bound by
        a pattern with constructor:
          Obj :: forall (cls :: * -> Constraint) o. cls o => o -> Obj cls,
        in an equation for ‘call’
        at existential.hs:31:9-13
    • In the expression: f k
      In an equation for ‘call’: call f (Obj k) = f k
    • Relevant bindings include
        k :: o (bound at existential.hs:31:13)
        f :: p (bound at existential.hs:31:6)
        call :: p -> Obj cls -> p1 (bound at existential.hs:31:1)
   |
31 | call f (Obj k) = f k
   |                  ^^^
Failed, no modules loaded.

我有点明白为什么会发生这种情况。但是对于像call foocall bar 这样的真正调用,类型变量不会逃逸。我可以说服编译器吗?也许我能以某种方式表达u -> v where v does not mention u 的类型(它真的应该是f 的类型)?如果不是,还有什么其他方法可以处理这种情况?我想我可以使用 TemplateHaskell 生成一些东西,但我仍然无法理解它。

【问题讨论】:

  • 您能更具体地说明您要做什么吗? op 是什么?
  • 你正在做的事情是可能的。您没有向我们展示的代码中某处存在更详细的错误。
  • @luqui 这是我要编译的确切代码,这是唯一的错误。
  • @dfeuer 这是我尝试用子类型探索一个小对象系统(如面向对象编程)。 op,或者更确切地说是flip op,将是一个方法选择器,就像典型 OOP 语言的 object . method 中的 .。我知道这可能不是最好的方法......

标签: haskell types typeclass


【解决方案1】:

您的代码运行良好;编译器只需要一些关于它的类型的帮助。

Obj 隐藏了其内容的类型,这意味着op 的参数f 必须是多态的(也就是说,它不能检查它的参数)。开启RankNTypes

op :: (forall a. cls a => a -> r) -> Obj cls -> r
op f (Obj x) = f x

您必须完整地给出类型签名,因为 GHC 无法推断出更高级别的类型。

对这样的类进行现有量化是usually not the best way 来设计一个给定的程序。

【讨论】:

    猜你喜欢
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多