约束约束的参数

时间:2017-11-17 14:45:18

标签: haskell typeclass

我有第一个类型类,它接受leaf的......列表列表:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
class ListTree leaf t where
  lmap :: (leaf -> leaf) -> t -> t
instance ListTree leaf leaf where lmap f v = f v
instance ListTree leaf t => ListTree leaf [t] where lmap f v = map (lmap f) v

我有第二个类型类接受a的2元组和3元组:

class Tups a t where
  tmap :: (a -> a) -> t -> t
instance Tups a (a,a) where tmap f (x,y) = (f x, f y)
instance Tups a (a,a,a) where tmap f (x,y,z) = (f x, f y, f z)

我想将它们组合起来描述以某些leaf类型的2或3元组结尾的嵌套列表:

class LTTree leaf t where
  ltmap :: (a -> a) -> t -> t
instance (Tups leaf x, ListTree x t) => LTTree leaf t where ltmap f v = lmap (tmap f) v

然而,最后一段代码给了我几个错误:

Could not deduce (LTTree leaf0 t)
  from the context: LTTree leaf t

In the ambiguity check for ‘ltmap’
  To defer the ambiguity check to use sites, enable AllowAmbiguousTypes

Could not deduce (Tups leaf x0)
  from the context: (Tups leaf x, ListTree x t)

In the ambiguity check for an instance declaration
  To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
  In the instance declaration for ‘LTTree leaf t’

如果我添加AllowAmbiguousTypes,我仍然会遇到类似的错误。

我可以通过内联其他两个类型类的代码来定义LTTree类,但是:

class LTTree leaf t where
  ltmap :: (leaf -> leaf) -> t -> t
instance LTTree leaf (leaf,leaf) where ltmap f (x,y) = (f x, f y)
instance LTTree leaf (leaf,leaf,leaf) where ltmap f (x,y,z) = (f x, f y, f z)
instance LTTree leaf t => LTTree leaf [t] where ltmap f v = map (ltmap f)

如何将ListTree leaf t类与Tups a t类合并,以便列表树的叶子是a的2或3元组?我不介意添加额外的GHC扩展,如果这可以帮助。

如果重要的话,我真正的用例是对列表树进行建模,其中叶子是行多态记录(使用CTRex),其中记录中的每个字段都是某个类型类的实例(例如Show,打印树木。)

1 个答案:

答案 0 :(得分:2)

你有另一个问题。你的ListTree课程没用!

> lmap id [5 :: Integer]
error: blah blah
> lmap id (5 :: Integer)
error: blah blah
> lmap (+2) [[5::Integer], [], [2,3]]
error: blah blah

添加一些黑魔法来解决这个问题:

{-# LANGUAGE FunctionalDependencies, GADTs #-}
class ListTree leaf tree where lmap :: (leaf -> leaf) -> (tree -> tree)
instance {-# OVERLAPPABLE #-} (leaf ~ tree) => ListTree leaf tree where -- 1
  lmap = id
instance ListTree leaf tree => ListTree leaf [tree] where -- 2
  lmap = map . lmap

(a ~ b)等式约束;当ab属于同一类型时,它会成立。它需要GADTs或{{ 1}}待用。)

根据http://localhost:8050,在检查TypeFamilies时,GHC会遇到这两个实例并发现它们都可以实例化:lmap id [5 :: Integer] 1leaf = [Integer]tree = [Integer]2leaf = Integer。要选择一个,它会检查tree = [Integer]的实例化是否对2有效。那就是:1leaf = Integertree = [Integer]的有效实例吗?答案是肯定的,因为具有平等约束的上下文直到稍后才被检查。然后,它会检查1 / OVERLAPPABLE / OVERLAPPING个pragma。如果有更好的实例,OVERLAPS实例会被丢弃。在这种情况下,OVERLAPPABLE被丢弃,只剩下1。它被使用,所以2。其他例子也有效。

lmap id [5 :: Integer] == [5],你有一个错字。它应该是:

LTTree

class LTTree leaf tree where ltmap :: (leaf -> leaf) -> tree -> tree ,而不是leaf。你还有另外一个问题:推理器因为让它完成所有这些工作而非常生气:

a

启用> instance (Tups leaf x, ListTree x t) => LTTree leaf t where ltmap f v = lmap (tmap f) v error: blah blah ScopedTypeVariables以帮助它:

TypeApplications

(或者只是使用{-# LANGUAGE ScopedTypeVariables, TypeApplications #-} instance (Tups leaf x, ListTree x t) => LTTree leaf t where ltmap f v = lmap @x @t (tmap @leaf @x f) v 明确地给出类型,但这很痛苦)

但更好的想法是启用::并开始喷涂它们,因为它们代表了类型级计算的想法:类型类的参数的某些子集可以唯一地确定其他参数。这将产生最终版本:

FunctionalDependencies

它有效!

{-# LANGUAGE FlexibleInstances
           , FunctionalDependencies
           , GADTs
           , UndecidableInstances #-}
class ListTree leaf tree | tree -> leaf where lmap :: (leaf -> leaf) -> tree -> tree
instance {-# OVERLAPPABLE #-} (leaf ~ tree) => ListTree leaf tree where lmap = id
instance ListTree leaf tree => ListTree leaf [tree] where lmap = map . lmap
-- The tree determines the leaf

class Tups leaf tree | tree -> leaf where tmap :: (leaf -> leaf) -> tree -> tree
-- Change instances to help type inference along:
instance (a ~ b) => Tups a (a, b) where tmap f (x, y) = (f x, f y)
instance (a ~ b, b ~ c) => Tups a (a, b, c) where tmap f (x, y, z) = (f x, f y, f z)
-- tmap (+2) (5 :: Integer, 3, 2) now works because the type info from 5 spreads out
-- via the equality constraints

class LTTree leaf tree | tree -> leaf where ltmap :: (leaf -> leaf) -> tree -> tree
instance (Tups leaf mid, ListTree mid tree) => LTTree leaf tree where ltmap = lmap . tmap
-- mid can be deduced from tree via ListTree's fundep
-- leaf can be deduced from mid via Tups' fundep
-- leaf can be deduced from tree