Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.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 类型签名过于笼统;缺少约束?_Haskell_Types - Fatal编程技术网

Haskell 类型签名过于笼统;缺少约束?

Haskell 类型签名过于笼统;缺少约束?,haskell,types,Haskell,Types,所以我试着问一个一般性的“”问题,似乎这个问题太一般而无法回答,或者可能无法调试这些东西。:-} 在这个特殊的日子里,让我恼火的问题是一个中等规模、复杂的程序。我已经设法把它归结为一个相当小的核心,仍然出错。我们开始: {-# LANGUAGE GADTs, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, RankNTypes, KindSignatures #-} module Minimal

所以我试着问一个一般性的“”问题,似乎这个问题太一般而无法回答,或者可能无法调试这些东西。:-}

在这个特殊的日子里,让我恼火的问题是一个中等规模、复杂的程序。我已经设法把它归结为一个相当小的核心,仍然出错。我们开始:

{-#
  LANGUAGE
    GADTs, MultiParamTypeClasses,
    FlexibleInstances, FlexibleContexts, RankNTypes,
    KindSignatures
#-}

module Minimal where

-------------------------------------------------------------------------------

data Union (t :: * -> *) (ts :: * -> *) x

class Member (x :: * -> *) (ys :: * -> *) where
instance Member x (Union x ys) where
instance (Member x ys) => Member x (Union y ys) where

-------------------------------------------------------------------------------

data ActM (effects :: * -> *) x
instance Monad (ActM effects) where

-------------------------------------------------------------------------------

data Reader i x where
  Get :: Reader i i

get :: Member (Reader i) req => ActM req i
get = undefined

runReader :: i -> ActM (Union (Reader i) req) x -> ActM req x
runReader = undefined
(旁白:我看着语言扩展列表,心里想:“也许这只是个糟糕的主意!”

无论如何,真正有趣的部分在底部。我们看到了

runReader :: i -> ActM (Union (Reader i) req) x -> ActM req x
换句话说,对于任何
i
runReader
ActM
monad中执行一个动作,该动作将
Reader i
作为其可能的效果之一,并在
ActM
monad中返回一个动作而不产生该可能的效果。很简单,对吧

现在,如果我传递(比如)一个
Char
作为第一个参数,那么读取器类型固定为
Char

runReader 'X' :: ActM (Union (Reader Char) req) x -> ActM req x
到目前为止,一切顺利。如果我只返回一些数据,一切正常:

runReader 'X' (return True) :: ActM req Bool
(令人欣慰的是,我也得到了正确的值作为结果!)

但是现在,关于
get
函数呢

get :: Member (Reader i) req => ActM req i
因此,对于包括
Reader i
的任何一组效果,
get
ActM
monad中的一个操作。这意味着如果我将它传递给
runReader
,那么我会得到

runReader 'X' get :: Member (Reader x) (Union (Reader Char) req) => ActM req x
等等,什么?!?为什么编译器没有计算出
x
必须是
Char

事实上,如果我添加一个显式类型签名

runReader 'X' get :: ActM req Char

然后它可以完美地编译(顺便说一句,我得到了正确的值输出,这很好)。那么,我在哪里遗漏了一个约束呢?

编译器没有计算出
x
必须是
Char
,因为它不是真的。例如,你可以写

type ReadsBool req = Union (Reader Bool) req
type ReadsCharAndBool req = Union (Reader Char) (ReadsBool req)
然后你有:

runReader 'X' (get :: ReadsCharAndBool req) :: ActM (ReadsBool req) Bool

恭喜你,现在你知道为什么fundep有它的功能了:出于类型推断的原因,人们通常希望monad唯一地确定你可以从它
得到什么样的东西。

实例
成员x(Union x ys)
要求两个
x
s从相同开始匹配-编译器不会推断此信息。您必须编写
(x~x')=>成员x(Union x'xs)
,但这将与其他实例重叠-我相信您需要一种更聪明的计算成员身份的方法。这真的足够扩展吗?这两个
成员
实例看起来非常可疑——可能你需要
重叠实例
,或者甚至可能需要
不连贯性
,才能“工作”。如果你打开了
不连贯性
,那么。。。我认为该扩展的官方政党路线是“祝你好运,不要向我们抱怨。”@DanielWagner,这很奇怪<代码>重叠实例
应该在其中。我不知道它怎么会不见了。我一定是在构建我的最小示例时意外地丢失了它。@user2407038我很惊讶地发现,您实际上可以在类型签名中编写
x~y
!你每天都在学习新的东西……那么你到底在建议我在哪里需要一个fundep?@MathematicalArchid在你脑海中出现的代码中没有一个小的改变可以解决问题。我所能做的一切都是一次彻底的架构改变。从类型推断的角度来看,您的
成员
类型类是“坏的”:
成员x(Union y ys)
实例始终可以匹配;更糟糕的匹配几乎没有告诉你类型
x
y
ys
,因为开放世界假设说任何人都可以稍后加入
成员xys
实例,用于任何愚蠢的类型对!您可以考虑将其拆分为<代码>读取器成员类(类似于其他效果)。