约束约束的论点
我有接受的名单...的名单列表第一类型类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
我有接受2元组和三元组的第二类型类a
:
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 at
类,以便列出的树的叶子是2或3元组a
? 如果可以提供帮助,我不介意增加额外的GHC扩展。
如果它很重要,我的实际使用情况是对树的列表树进行建模,其中叶是行多态记录(使用CTRex),其中记录中的每个字段都是某种类型类的实例(例如Show
,以打印树)。
你有另一个问题。 你的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)
GADTs
(a ~ b)
是一个等式约束;当a
和b
是相同类型时,它就成立a
,它需要使用GADTs
或TypeFamilies
。)
根据实例解析的规则,当检查lmap id [5 :: Integer]
,GHC会遇到两个实例并且发现它们都可以被实例化: 1
与leaf = [Integer]
和tree = [Integer]
, 2
与leaf = Integer
和tree = [Integer]
。 要选择一个,它会检查2
的实例是否对1
有效。 那就是:是leaf = Integer
, tree = [Integer]
是1
的有效实例吗? 答案是肯定的,因为直到后来才会检查具有平等限制的上下文。 然后,它检查OVERLAPPABLE
/ OVERLAPPING
/ 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
它的工作原理!
> ltmap (+(2 :: Integer)) [[[(5, 2)]], [], [[(2, 8), (4, 5)]]]
[[[(7,4)]],[],[[(4,10),(6,7)]]]
链接地址: http://www.djcxy.com/p/43543.html