简体   繁体   English

试图使我的typeclass / instance。 GHC说:“无法推断……”

[英]Trying to make my typeclass/instance. GHC says “Could not deduce…”

I am trying to make a simple graph structure and I wrote the following. 我试图制作一个简单的图形结构,并编写了以下内容。 But GHG raises error and I stacked there. 但是GHG引发了错误,因此我在那堆了。 This is the first time I make my own typeclass so maybe I am doing something terribly wrong. 这是我第一次进行自己的类型分类,所以也许我做错了什么。 Can somebody explain what is wrong? 有人可以解释什么地方不对吗?

I found a similar question but I don't think it applies to my case.: Error binding type variables in instance of typeclass 我发现了一个类似的问题,但我认为这不适用于我的情况。: 在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

This is the error message I've got: 这是我收到的错误消息:

***.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

Edit 1 编辑1

I tried some of Daniel's suggestions . 我尝试了丹尼尔的一些建议 But I couldn't make them work. 但是我不能让它们工作。

constructor class 构造器类

Got: "`n' is not applied to enough type arguments" 得到:“`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]

multi-parameter type class (MPTC) 多参数类型类别(MPTC)

Got: "Cycle in class declarations (via superclasses)" 得到:“循环类声明(通过超类)”

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

MPTC with functional dependencies 具有功能依赖性的MPTC

Got: "Cycle in class declarations (via superclasses)" 得到:“循环类声明(通过超类)”

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

Goal (Edit 2) 目标(编辑2)

What I want to implement is a directed acyclic graph structure like the following (more specifically, a Factor graph ). 我要实现的是有向无环图结构,如下所示(更具体地讲,为Factor图 )。

PRML图8.51
(source: microsoft.com ) (来源: microsoft.com

There are two kinds of node (white circle and red square) and they connect only to the different type of node, meaning that there are two kinds of links. 有两种类型的节点(白色圆圈和红色正方形),它们仅连接到不同类型的节点,这意味着存在两种链接。

I want different version of nodes and links which have data (arrays) attached to them. 我想要具有附加数据(数组)的节点和链接的不同版本。 I also want "vanilla" DAG which has only one type of node and link. 我还想要“ vanilla” DAG,它只有一种类型的节点和链接。 But for traversing them, I want only one interface to do that. 但是对于遍历它们,我只需要一个接口即可完成。

The signature of the class methods 类方法的签名

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

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

say that "whatever type the caller desires, node resp. links can produce it, as long as it's a member of Link resp. Node ", but the implementation says that only one specific type of value can be produced. 说,“无论何种类型的来电者的欲望, node RESP。 links可以生产它,只要它的成员Link RESP。 Node ”,但执行说,只有一个有价值的特定类型的都可以生产。

It's fundamentally different from interfaces in OOP, where the implementation decides the type and the caller has to take it, here the caller decides. 它与OOP中的接口从根本上不同,在OOP中,实现由类型决定,调用者必须采用它,而调用者在这里进行决定。


You are running into kind problems with your constructor class attempt. 您在构造函数类尝试中遇到了种种问题。 Your classes take two parameters, l of kind kl and n of kind kn . 您的类采用两个参数,种类为kl l和种类为kn n The kinds of the arguments to (->) must both be * , the kind of types. (->)的参数种类都必须是* ,即类型。 So for ln to be a well-kinded argument of (->) , l must be a type constructor taking an argument of kind kn and creating a result of kind * , ie 因此,要使ln成为(->)的良好参数, l必须是一个类型构造函数,并接受类型kn的参数并创建类型*的结果,即

l :: kn -> *

Now you try to make the result type of node be nl , so that would mean 现在,您尝试使node的结果类型为nl ,这意味着

n :: kl -> *

But above we saw that kl = kn -> * , which yields 但在上面我们看到kl = kn -> * ,得出

n :: (kn -> *) -> *

resp. 分别 kn = (kn -> *) -> * , which is an infinite kind. kn = (kn -> *) -> * ,这是一种无限的类型。 Infinite kinds, like infinite types, are not allowed. 不允许使用无限类型,例如无限类型。 But kind-inference is implemented only very rudimentary, so the compiler assumes that the argument to l has kind * , but sees from nl that n has kind kl -> * , hence as an argument to l , n has the wrong kind, it is not applied to enough type arguments. 但是种类推断仅是非常基本的实现,因此编译器假定l的参数为kind * ,但从nl看到n种类为kl -> * ,因此作为l的参数, n的种类错误,因此不适用于足够的类型参数。

The normal use of constructor classes is a single-parameter class 构造函数类的通常用法是单参数类

class Link l where
    node :: l nod -> nod

class Node n where
    links :: n lin -> [lin]

-- note that we don't have constraints here, because the kinds don't fit

instance Link SimpleLink where
    node = simpleLinkNode

instance Node SimpleNode where
    links = simpleNodeLinks

You have to remove the DatatypeContexts from the data declarations, 您必须从数据声明中删除DatatypeContexts

  1. They have been removed from the language (they are available via an extension) 它们已从语言中删除(可通过扩展名获得)
  2. They were never useful anyway 无论如何它们从来没有用

then the above compiles. 然后以上编译。 I don't think it would help you, though. 不过,我认为这不会帮助您。 As Chris Kuklewicz observed, your types chase their own tail, you'd use them as 正如克里斯·库克莱维奇(Chris Kuklewicz)观察到的那样,您的类型会追逐自己的尾巴,您将它们用作

SimpleLink (SimpleNode (SimpleLink (SimpleNode ... {- ad infinitum -})))

For the multiparameter classes, you can't have each a requirement of the other, as the compiler says, that causes a dependency cycle (also, in your constraints you use them with only one parameter, 正如编译器所说,对于多参数类,您不能每个都具有另一个需求,这会导致一个依赖循环(此外,在约束中,您只能将它们与一个参数一起使用,

class Node n => Link l n where ...

which is malformed, the compiler would refuse that if the cycle is broken). 格式错误,如果周期中断,编译器将拒绝)。

You could resolve the cycle by merging the classes, 您可以通过合并类来解决周期,

class NodeLinks l n | l -> n, n -> l where
    node :: l -> n
    links :: n -> l

but you'd still have the problems that your types aren't useful for that. 但是您仍然会遇到类型对您没有用的问题。

I don't understand your goal well enough to suggest a viable solution, sorry. 很抱歉,我对您的目标不太了解,无法提出可行的解决方案。

Can somebody explain what is wrong? 有人可以解释什么地方不对吗?

An initial issue before I explain the error messages: Polymorphic data types are good, but in the end there has to be concrete type being used. 在我解释错误消息之前,有一个最初的问题:多态数据类型很好,但是最后必须使用具体的类型。

With SimpleNode of kind * -> * and SimpleLinks of kind * -> * there is no concrete type: 随着那种SimpleNode的* -> *和种SimpleLinks * -> *没有具体的类型:

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

You cannot have and infinite type in Haskell, though newtype and data get you closer: 在Haskell中,不能具有无限类型,尽管新类型和数据使您更接近:

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

Perhaps you need to rethink your data types before creating the type class. 也许您需要在创建类型类之前重新考虑数据类型。

Now on to the error message explanation: Your type class Link defines a function node 现在继续对错误消息进行解释:您的类型类Link定义了一个功能node

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

The node is a magical OOP factory that, given the type and value of l , can then make any type n (bounded by Node n ) the caller of node wishes. node是一个神奇OOP工厂,鉴于类型和值l ,然后可以使任何类型的n (由边界Node n )的调用者 node的愿望。 This n has nothing to do with the n in your instance: n与您的实例中的n无关:

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

To repeat myself: the n in the instance above is not the same n as in the node :: (Node n) => l -> n definition. 重复自己:所述n在上面的实例中是不一样的n如在node :: (Node n) => l -> n定义。 The compiler makes a related but fresh name n1 and gives you the error: 编译器给出一个相关但新鲜的名称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

The n in the instance is taken from the type (SimpleLink n) of the input to the node function. 实例中的n取自node函数的输入类型(SimpleLink n)。 The n1 is the type that the caller of node is demanding that this magical factory produce. n1node的调用方要求该神奇工厂产生的类型。 If n and n1 were the same then the compiler would be happy...but your definition of the type class and instance do not constrain this and thus the code snippet is rejected. 如果n和n1相同,则编译器会很高兴...但是您对类型类和实例的定义不对此加以限制,因此代码段被拒绝。

The analogous story is repeated for the error in SimpleLink. 对于SimpleLink中的错误,将重复类似的故事。 There is no silver-bullet fix for this. 没有针对此问题的解决方案。 I expect that you need to rethink and redesign this, probably after reading other people's code in order to learn ways to accomplish your goal. 我希望您可能需要在阅读其他人的代码后重新思考和重新设计,以了解实现目标的方法。

What is your goal? 你的目标是什么? Graph data structures can be quite varied and the details matter. 图形数据结构可能会发生很大变化,并且细节很重要。

I am breaking stack overflow etiquette and adding a second answer to keep this separate. 我打破了堆栈溢出礼节,并添加了第二个答案以保持独立性。 This is a simple code example for a bipartite undirected graph with unlabeled edges, which might be useful to model a 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]

With the hint from Chris Kuklewicz (http://stackoverflow.com/a/11450715/727827), I got the code I wanted in the first place. 在Chris Kuklewicz(http://stackoverflow.com/a/11450715/727827)的提示下,我首先得到了想要的代码。

However, I think Crhis's answer (using *Vertex to hold data) is simple and better. 但是,我认为Crhis的答案(使用*Vertex来保存数据)既简单又好。 I am leaving this here to clarify what I wanted. 我将其留在此处以澄清我想要的内容。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM