代码之家  ›  专栏  ›  技术社区  ›  Christoph Schiessl Joeyjoejoejr

具有类型类约束构造函数的多态ADT

  •  1
  • Christoph Schiessl Joeyjoejoejr  · 技术社区  · 6 年前

    为什么以下代码无法编译?

    {-# LANGUAGE GADTs #-}
    
    class Foo x where whoAmI :: x -> Int
    
    data One = One
    instance Foo One where
      whoAmI _ = 1
    
    data Two = Two
    instance Foo Two where
      whoAmI _ = 2
    
    data Poly f = (Foo f) => Poly { member :: f }
    makePoly :: Bool -> Poly f
    makePoly what =
      if what == True then Poly { member = One }
      else Poly { member = Two }
    

    显然,这是一个人为的例子。最后,我想要一张唱片 Poly f 把它传递给其他函数,比如 g :: Poly f -> Int 在哪里 g 只能使用类给定的函数与它的参数进行交互 Foo .

    这是GHC V8.6.3编译错误:

    Couldn't match expected type ‘f’ with actual type ‘One’
    ‘f’ is a rigid type variable bound by
      the type signature for:
        makePoly :: forall f. Bool -> Poly f
    

    有没有可能做那样的事?编译它需要哪些扩展(如果有的话)?

    1 回复  |  直到 6 年前
        1
  •  1
  •   luqui    6 年前

    看起来你对存在主义有些困惑。你写的东西

    data Poly f = (Foo f) => Poly { member :: f }
    

    定义一系列类型,例如一种类型 Poly One A 不同类型 Poly Two 等时 Poly 是构造的,它的 Foo -检查状态。

    问题在于

    makePoly :: Bool -> Poly f
    

    那是 呼叫者 得到选择 f . 所以

    makePoly True :: Poly One
    makePoly True :: Poly Two
    makePoly True :: Poly Elephant
    

    所有这些都必须工作,但你的功能不是这样工作的。似乎你几乎知道自己在做什么,因为如果不是定义一个类型的族,而是定义一个 单一的 存在主义类型,那么这就很好了:

    data Poly = forall f. (Foo f) => Poly { member :: f }
    

    注意没有 f = 签名,所以这是一个单一的类型,而不是一个家庭。因此,一个函数声明返回 可以返回任何 它喜欢。这个定义使代码工作。

    技术说明 member 字段访问器是完全无用的,因为返回类型取决于 价值 通过

    member :: Poly -> ????
    

    因此不能在haskell类型系统中为其分配类型(但是依赖类型系统可以这样做)。使用A 必须 模式匹配:

    usePoly :: Poly -> Int
    usePoly (Poly x) = whoAmI x -- x :: a  for some *unknown* type a in this scope
    

    另一个音符, 完全等同于 Int ,因为如果我们 Poly x 我们所知道的 x 这是一个 我们唯一能做的就是 是呼叫 whoAmI . 在这种情况下,我只是 skip the existential 使用 int . 但是存在主义的有效运用,比人们想象的要普遍得多。