试图创建我的类型类/实例。 GHC说“无法演绎......”

时间:2012-07-10 20:26:20

标签: haskell instance typeclass

我正在尝试制作一个简单的图形结构,我写了以下内容。但温室气体引起了错误,我堆积在那里。这是我第一次制作自己的类型类,所以也许我做的事情非常糟糕。有人可以解释什么是错的吗?

我发现了类似的问题,但我不认为它适用于我的情况: Error binding type variables in instance of typeclass

class Link l where
  node :: (Node n) => l -> n

class Node n where
  links :: (Link l) => n -> [l]

data (Node n) => SimpleLink n =
  SimpleLink
  { simpleLinkNode :: n
  } deriving (Show, Read, Eq)

instance (Node n) => Link (SimpleLink n) where
  node = simpleLinkNode

data (Link l) => SimpleNode l =
  SimpleNode
  { simpleNodeLinks :: [l]
  } deriving (Show, Read, Eq)

instance (Link l) => Node (SimpleNode l) where
  links = simpleNodeLinks

这是我收到的错误消息:

***.hs:13:10:Could not deduce (n ~ n1)
from the context (Node n)
  bound by the instance declaration
  at ***.hs:12:10-40
or from (Node n1)
  bound by the type signature for
             node :: Node n1 => SimpleLink n -> n1
  at ***.hs:13:3-23
  `n' is a rigid type variable bound by
      the instance declaration
      at ***.hs:12:16
  `n1' is a rigid type variable bound by
       the type signature for node :: Node n1 => SimpleLink n -> n1
       at ***.hs:13:3
Expected type: SimpleLink n -> n1
  Actual type: SimpleLink n -> n
In the expression: simpleLinkNode
In an equation for `node': node = simpleLinkNode

***.hs:21:11:Could not deduce (l ~ l1)
from the context (Link l)
  bound by the instance declaration
  at ***.hs:20:10-40
or from (Link l1)
  bound by the type signature for
             links :: Link l1 => SimpleNode l -> [l1]
  at ***.hs:21:3-25
  `l' is a rigid type variable bound by
      the instance declaration
      at ***.hs:20:16
  `l1' is a rigid type variable bound by
       the type signature for links :: Link l1 => SimpleNode l -> [l1]
       at ***.hs:21:3
Expected type: SimpleNode l -> [l1]
  Actual type: SimpleNode l -> [l]
In the expression: simpleNodeLinks
In an equation for `links': links = simpleNodeLinks

编辑1

我尝试了一些Daniel's suggestions。 但是我无法让它们发挥作用。

构造函数类

得到:“`n'不适用于足够的类型参数”

class Link l n where
  node :: Node n l => l n -> n l
class Node n l where
  links :: Link l n => n l -> [l n]

多参数类型类(MPTC)

得到:“在类声明中循环(通过超类)”

class (Node n) => Link l n where
  node :: l -> n
class (Link l) => Node n l where
  links :: n -> [l]

具有功能依赖性的MPTC

得到:“在类声明中循环(通过超类)”

class (Node n) => Link l n | l -> n where
  node :: l -> n
class (Link l) => Node n l | n -> l where
  links :: n -> [l]

目标(编辑2)

我想要实现的是一个有向无环图结构,如下所示(更具体地说,是一个Factor graph)。

PRML Figure 8.51 http://research.microsoft.com/en-us/um/people/cmbishop/prml/prmlfigs-png/Figure8.51.png

有两种节点(白色圆圈和红色方块),它们只连接到不同类型的节点,这意味着有两种链接。

我想要不同版本的节点和连接数据(数组)的链接。我也想要“vanilla”DAG,它只有一种类型的节点和链接。但是为了遍历它们,我只想要一个接口来做到这一点。

4 个答案:

答案 0 :(得分:6)

类方法的签名

class Link l where
  node :: (Node n) => l -> n

class Node n where
  links :: (Link l) => n -> [l]

说“只要<{1}} resp。{{{ {1}}“,但实现表明只能生成一种特定类型的值。

它与OOP中的接口有根本的不同,OOP中的实现决定了类型,调用者必须接受它,调用者在这里决定。


您正在遇到构造函数类尝试的问题。您的类有两个参数,node种类linksLink种类Nodel的参数类型必须都是kl,类型的种类。因此,n要成为kn的良好论证,(->)必须是一个类型构造函数,它接受类*的参数并创建类型{{1}的结果,即

l n

现在您尝试将结果类型(->)设为l,这意味着

kn

但上面我们看到了*,它产生了

l :: kn -> *

RESP。 node,这是一种无限的。无限类型,如无限类型,是不允许的。但是,类推理只是非常基本的,所以编译器认为n l的参数有n :: kl -> * 种,但从kl = kn -> *n :: (kn -> *) -> * 有种类{{1}因此,作为kn = (kn -> *) -> *的参数,l具有错误的类型,它不适用于足够的类型参数。

构造函数类的正常使用是单参数类

*

您必须从数据声明

中删除n l
  1. 他们已从语言中删除(可通过扩展程序获取)
  2. 无论如何它们从未有用
  3. 然后上面的编译。不过,我认为它不会对你有所帮助。正如Chris Kuklewicz所说,你的类型追逐自己的尾巴,你将它们用作

    n

    对于多参数类,如编译器所说,你不能让对方的每个要求都导致依赖循环(同样,在你的约束中你只使用一个参数,

    kl -> *

    格式错误,如果循环被破坏,编译器会拒绝。

    您可以通过合并类来解决周期,

    l

    但是您仍然遇到类型对此无用的问题。

    我不太了解你的目标,建议一个可行的解决方案,抱歉。

答案 1 :(得分:4)

  

有人可以解释什么是错的吗?

在我解释错误消息之前的一个初始问题:多态数据类型很好,但最后必须使用具体类型。

使用类型* -> *的SimpleNode和类型* -> *的SimpleLinks,没有具体的类型:

SimpleNode (SimpleLink (SimpleNode (SimpleLink (SimpleNode (...

你不能拥有Haskell中的无限类型,尽管newtype和data会让你更接近:

type G0 = SimpleNode (SimpleLink G0)  -- illegal
newtype G1 = G1 (SimpleNode (SimpleLink G1))   -- legal
data G2 = G2 (SimpleNode (SimpleLink G2))   -- legal

在创建类型类之前,您可能需要重新考虑数据类型。

现在转到错误消息说明:您的类型类Link定义了一个函数node

class Link l where
  node :: (Node n) => l -> n

node是一个神奇的OOP工厂,根据l的类型和价值,可以制作任何类型n(由Node n限定) node愿望的来电者。此n与您实例中的n无关:

instance (Node n) => Link (SimpleLink n) where
  node = simpleLinkNode

重复一遍:上面实例中的nn定义中的node :: (Node n) => l -> n不同。编译器生成一个相关但新鲜的名称n1,并给出错误:

  `n' is a rigid type variable bound by
      the instance declaration
      at ***.hs:12:16
  `n1' is a rigid type variable bound by
       the type signature for node :: Node n1 => SimpleLink n -> n1
       at ***.hs:13:3

实例中的n取自node函数输入的类型(SimpleLink n)。 n1node的调用者要求这个神奇的工厂生产的类型。如果n和n1相同,那么编译器会很高兴...但是你对类型类和实例的定义不会限制它,因此代码片段会被拒绝。

SimpleLink中的错误重复了类似的故事。没有银弹可以解决这个问题。我希望你需要重新思考和重新设计这个,可能是在阅读了其他人的代码之后,以便学习如何实现目标。

你的目标是什么?图形数据结构可以变化很多,细节也很重要。

答案 2 :(得分:1)

我正在打破堆栈溢出礼仪并添加第二个答案以保持这一点。这是一个带有未标记边的二分无向图的简单代码示例,可能对Factor Graph建模有用:

-- Bipartite graph representation, unlabeled edges

-- Data types to hold information about nodes, e.g. ID number
data VariableVertex = VV { vvID :: Int }  deriving (Show)
data FactorVertex = FV { fvID :: Int }  deriving (Show)

-- Node holds itself and a list of neighbors of the oppostite type
data Node selfType adjacentType =
  N { self :: selfType
    , adj :: [Node adjacentType selfType] }

-- A custom Show for Node to prevent infinite output
instance (Show a, Show b) => Show (Node a b) where
  show (N x ys) = "Node "++ show x ++ " near " ++ show (map self ys)

-- Type aliases for the two node types that will be used
type VariableNode = Node VariableVertex FactorVertex
type FactorNode = Node FactorVertex VariableVertex

data FactorGraph = FG [VariableNode] [FactorNode]  deriving (Show)

v1 = N (VV 1) [f1,f2]
v2 = N (VV 2) [f2]
v3 = N (VV 3) [f1,f3]
f1 = N (FV 1) [v1,v3]
f2 = N (FV 2) [v1,v2]
f3 = N (FV 3) [v3]

g = FG [v1,v2,v3] [f1,f2,f3]

答案 3 :(得分:0)

借助Chris Kuklewicz(http://stackoverflow.com/a/11450715/727827)的提示,我首先得到了我想要的代码。

但是,我认为Crhis的回答(使用*Vertex来保存数据)简单而且更好。我要离开这里澄清我想要的东西。

class NodeClass n where
  adjacent :: n a b -> [n b a]

data Node selfType adjacentType =
  N
  { selfNode :: selfType
  , adjNode :: [Node adjacentType selfType] }

data NodeWithData selfType adjacentType =
  NWD
  { selfNodeWithData :: selfType
  , adjNodeWithData :: [NodeWithData adjacentType selfType]
  , getDataWithData :: [Double]
  }

instance NodeClass Node where
  adjacent = adjNode

instance NodeClass NodeWithData where
  adjacent = adjNodeWithData

data VariableVertex = VV { vvID :: Int }  deriving (Show)
data FactorVertex = FV { fvID :: Int }  deriving (Show)

type VariableNode = Node VariableVertex FactorVertex
type FactorNode = Node FactorVertex VariableVertex

type VariableNodeWithData = NodeWithData VariableVertex FactorVertex
type FactorNodeWithData = NodeWithData FactorVertex VariableVertex