Haskell 重叠实例以展平元组

Haskell 重叠实例以展平元组,haskell,functional-dependencies,Haskell,Functional Dependencies,我正试图编写代码来从元组链中删除空元组。编译器拒绝该程序: 代码: 目标: 好的,首先:编译器抱怨fundeps冲突的原因是。。。因为他们确实有冲突。事实上,这是无法避免的,因为冲突是你试图做的事情中固有的。第一个类型参数是“input”,您基本上是针对特定类型在其上进行模式匹配,重叠给出了默认的fall-through案例。但是,第二个“输出”类型参数需要根据“输入”的不同而有所不同,具体情况与默认情况不同,因此会产生冲突 为了解决这个问题,您需要使用一些技巧,利用GHC在选择实例时只检查实

我正试图编写代码来从元组链中删除空元组。编译器拒绝该程序:

代码:


目标:


好的,首先:编译器抱怨fundeps冲突的原因是。。。因为他们确实有冲突。事实上,这是无法避免的,因为冲突是你试图做的事情中固有的。第一个类型参数是“input”,您基本上是针对特定类型在其上进行模式匹配,重叠给出了默认的fall-through案例。但是,第二个“输出”类型参数需要根据“输入”的不同而有所不同,具体情况与默认情况不同,因此会产生冲突

为了解决这个问题,您需要使用一些技巧,利用GHC在选择实例时只检查实例头的事实,然后稍后检查上下文以应用其他约束。这个技巧的核心是完全不指定“output”类型,因此实例选择只检查第一个参数,并认为第二个参数对于所有实例都是相同的,同时在上下文中插入一些东西,将第二个参数与事实之后所需的“output”统一起来

在当前GHC版本中使用此技术的最简单方法是启用类型族以获得
~
相等约束功能。下面是一个例子:

instance (() ~ r) => Flatten (() :* ()) r where
  flatten _ = ()

instance (Flatten a r) => Flatten (() :* a) r where
  flatten (_ :* rest) = flatten rest

instance (a ~ r) => Flatten (a :* ()) r where
  flatten (x :* _) = x

instance ((a :* c) ~ r, Flatten b c) => Flatten (a :* b) r where
  flatten (x :* rest) = (x :* flatten rest)

instance (a ~ r) => Flatten a r where
  flatten x = x
为了说明我让每个实例使用的模式,即使不是绝对必要,也要使用这个技巧。我们可以定义您想要的输入:

test = (0 :* () :* 1 :* 2 :* 3 :* () :* () :*4 :* ()) 
然后,在GHCi中:

∀x. x ⊢ flatten test
0 :* (1 :* (2 :* (3 :* 4)))
现在,您可能想知道为什么我在GHCi之外定义了
test
。不幸的是,如果将上述实例应用于多态输入类型,则仍然会失败,并且从文件加载它会导致单态限制和类型默认将所有数字文本转换为
整数
。但是,对于这种模糊性,确实没有什么可以做的,因为可以匹配多个输入的类型参数确实是模糊的



作为一个历史记录,你可以不用
~
,只使用fundeps和GHC的怪异怪癖就可以完成同样的把戏。许多可笑的类型黑客需要这种方法的某些版本,而原始版本(毫不奇怪)是由Oleg发明的,使用了稍微有点误导性的名称
TypeCast
,并被用于实现等式谓词
TypeEq
,它是HList之类的东西的基础。

为什么要使用元组而不是列表?这似乎是一种非常困难的方法来完成一些非常简单的事情。这是一个准引号,它应用了一个允许返回混合类型的函数。我想删除
()
。您能否澄清它以何种方式不起作用,以及您希望它做什么?这样的重叠在多态类型上使用时总是有点脆弱。
instance (() ~ r) => Flatten (() :* ()) r where
  flatten _ = ()

instance (Flatten a r) => Flatten (() :* a) r where
  flatten (_ :* rest) = flatten rest

instance (a ~ r) => Flatten (a :* ()) r where
  flatten (x :* _) = x

instance ((a :* c) ~ r, Flatten b c) => Flatten (a :* b) r where
  flatten (x :* rest) = (x :* flatten rest)

instance (a ~ r) => Flatten a r where
  flatten x = x
test = (0 :* () :* 1 :* 2 :* 3 :* () :* () :*4 :* ()) 
∀x. x ⊢ flatten test
0 :* (1 :* (2 :* (3 :* 4)))