Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell:为zipper创建类型类_Haskell_Design Patterns_Typeclass_Zipper - Fatal编程技术网

Haskell:为zipper创建类型类

Haskell:为zipper创建类型类,haskell,design-patterns,typeclass,zipper,Haskell,Design Patterns,Typeclass,Zipper,因此,我一直在阅读Haskell(我想还有其他函数式语言)中的Zipper模式来遍历和修改数据结构,我认为这将是一个很好的机会来磨练我在Haskell中创建类型类的技能,因为 该类可以为我提供一个公共的遍历接口来编写代码,与遍历的数据结构无关 我想我可能需要两个类——一个用于根数据结构,另一个用于创建的特殊数据结构 要遍历第一个: module Zipper where class Zipper z where go'up :: z -> Maybe z go'down :: z

因此,我一直在阅读Haskell(我想还有其他函数式语言)中的Zipper模式来遍历和修改数据结构,我认为这将是一个很好的机会来磨练我在Haskell中创建类型类的技能,因为 该类可以为我提供一个公共的遍历接口来编写代码,与遍历的数据结构无关

我想我可能需要两个类——一个用于根数据结构,另一个用于创建的特殊数据结构 要遍历第一个:

module Zipper where

class Zipper z where
  go'up :: z -> Maybe z
  go'down :: z -> Maybe z
  go'left :: z -> Maybe z
  go'right :: z -> Maybe z

class Zippable t where
  zipper :: (Zipper z) => t -> z
  get :: (Zipper z) => z -> t
  put :: (Zipper z) => z -> t -> z
但当我尝试使用一些简单的数据结构(如列表)时:

-- store a path through a list, with preceding elements stored in reverse
data ListZipper a = ListZipper { preceding :: [a], following :: [a] }

instance Zipper (ListZipper a) where
  go'up ListZipper { preceding = [] } = Nothing
  go'up ListZipper { preceding = a:ps, following = fs } = 
      Just $ ListZipper { preceding = ps, following = a:fs }
  go'down ListZipper { following = [] } = Nothing
  go'down ListZipper { preceding = ps, following = a:fs } = 
      Just $ ListZipper { preceding = a:ps, following = fs }
  go'left _ = Nothing
  go'right _ = Nothing

instance Zippable ([a]) where
  zipper as = ListZipper { preceding = [], following = as }
  get = following
  put z as = z { following = as }
或二叉树:

-- binary tree that only stores values at the leaves
data Tree a = Node { left'child :: Tree a, right'child :: Tree a } | Leaf a
-- store a path down a Tree, with branches not taken stored in reverse
data TreeZipper a = TreeZipper { branches :: [Either (Tree a) (Tree a)], subtree :: Tree a }

instance Zipper (TreeZipper a) where
  go'up TreeZipper { branches = [] } = Nothing
  go'up TreeZipper { branches = (Left l):bs, subtree = r } =  
      Just $ TreeZipper { branches = bs, subtree = Node { left'child = l, right'child = r } }
  go'up TreeZipper { branches = (Right r):bs, subtree = l } =  
      Just $ TreeZipper { branches = bs, subtree = Node { left'child = l, right'child = r } }
  go'down TreeZipper { subtree = Leaf a } = Nothing
  go'down TreeZipper { branches = bs, subtree = Node { left'child = l, right'child = r } } =
      Just $ TreeZipper { branches = (Right r):bs, subtree = l }
  go'left TreeZipper { branches = [] } = Nothing
  go'left TreeZipper { branches = (Right r):bs } = Nothing
  go'left TreeZipper { branches = (Left l):bs, subtree = r } =
      Just $ TreeZipper { branches = (Right r):bs, subtree = l }
  go'right TreeZipper { branches = [] } = Nothing
  go'right TreeZipper { branches = (Left l):bs } = Nothing
  go'right TreeZipper { branches = (Right r):bs, subtree = l } =
      Just $ TreeZipper { branches = (Left l):bs, subtree = r }

instance Zippable (Tree a) where
  zipper t = TreeZipper { branches = [], subtree = t }
  get TreeZipper { subtree = s } = s
  put z s = z { subtree = s }
我无法将其编译,我的每个
Zippable
实例定义都会出现很多类似这样的错误:

Zipper.hs:28:14: Couldn't match expected type `z' against inferred type `ListZipper a' `z' is a rigid type variable bound by the type signature for `zipper' at Zipper.hs:10:20 In the expression: ListZipper {preceding = [], following = as} In the definition of `zipper': zipper as = ListZipper {preceding = [], following = as} In the definition for method `zipper' 拉链。hs:28:14: 无法匹配预期的类型“z” 针对推断类型“ListA” `z'是一个刚性类型变量,由 拉链处“拉链”的类型签名。hs:10:20 在表达式中:listzippers{previous=[],following=as} 在“拉链”的定义中: zippers as=listzippers{previous=[],following=as} 在“zipper”方法的定义中 所以我不知道接下来该怎么办。我怀疑我的问题是我试图绑定这两个实例 总之,当
(Zipper z)=>
声明只想
z
成为任何
Zipper

时(旁白:你的
go'up
命名方案是……有创意的。哈斯克尔风格通常是camelCase。)

你在正确的轨道上。你所写的与下面的内容相当

{-# LANGUAGE RankNTypes #-}
instance Zippable [a] where
    zipper = ... :: forall z. (Zipper z) => [a] -> z
    get = ... :: forall z. (Zipper z) => z -> [a]
    set = ... :: forall z. (Zipper z) => z -> [a] -> z
(对于所有类型的
z
,给定
拉链z
,存在一个
拉链::[a]->z

您正在尝试定义
zippers=…::[a] ->Listzippers a
,这显然限制太多

您的代码将通过以下最小更改进行类型检查:

{-# LANGUAGE MultiParamTypeClasses #-}
class (Zipper z) => Zippable z t where
    zipper :: t -> z
    get :: z -> t
    set :: z -> t -> z
instance Zippable (ListZipper a) [a] where
    ...
instance Zippable (TreeZipper a) (Tree a) where
    ...

看。它是Haskell'98之后的扩展,但Haskell实现广泛支持它。

您也可以使用类型同义词族,而不是多参数类型类和函数依赖项。在这种情况下,它们提供了一种更干净、更容易理解的解决方案。在这种情况下,类和实例将成为:

class Zippable t where
  type ZipperType t :: *
  enter :: t -> ZipperType t
  focus :: ZipperType t -> t

instance Zippable [a] where
  type ZipperType [a] = ListZipper a
  enter = ...
  focus = ...
对于已经熟悉Haskell的人来说,这是一个关于类型同义词族的极好介绍。不久前,我还写过如何经常使用类型同义词族来代替函数依赖项


希望这有帮助

+1/接受-非常感谢!我正在慢慢地学习Haskell,实际上还没有学习命名约定,但我会做到的。OT:我什么时候应该在名称中使用撇号?我只看到它被用作“prime”。类似于
让x'=x+1
。应该使用它来命名对旧值稍作修改的值。在数学中使用撇号后,撇号仅用于名称的末尾,并用于命名相关值。将函数依赖项t->z添加到Zippable可能是个好主意。否则,当您尝试使用这些类时,您将遇到类型歧义。。。(另请参见)类型族是在GHC 6.10.1左右引入的?我还没有真正使用它们,但它们似乎很方便。使用zippers作为状态变量添加Monad实例如何。然后交换两个项目,你说“x这就是我今晚做的:)