当前位置: 首页 > 知识库问答 >
问题:

为什么GHC不承认函数是线性的?

阎建华
2023-03-14

我有一个非常简单的片段:

{-# LANGUAGE LinearTypes #-}

module Lib where

data Peer st = Peer { data :: String } deriving Show

data Idle
data Busy

sendToPeer :: Peer Idle %1-> Int -> IO (Peer Busy)
sendToPeer c n = case c of Peer d -> pure $ Peer d

我在解析器上:ghc-9.0.1

从文件中:

函数f是线性的,如果:当它的结果只消耗一次,那么它的参数只消耗一次。直观地说,这意味着在f定义的每一个分支中,它的参数x必须只使用一次。这可以通过

  • 返回x未修改
  • 将x传递给线性函数
  • 在x上进行模式匹配,并以相同的方式使用每个参数一次。
  • 将其作为函数调用,并以相同的方式使用结果一次。

我的函数sendToPeer就是这样做的——模式匹配于c,它的参数d对等d中使用一次,这是线性的:

默认情况下,代数数据类型中的所有字段都是线性的(即使-XLinearTypes未打开)。

但我有个错误:

• Couldn't match type ‘'Many’ with ‘'One’
        arising from multiplicity of ‘c’
• In an equation for ‘sendToPeer’:
          sendToPeer c n = case c of { Peer d -> pure $ Peer d }
   |
11 | sendToPeer c n =
   |            ^

没有纯:

sendToPeer :: Peer Idle %1-> Int -> Peer Busy
sendToPeer c n = case c of Peer d -> Peer d

但错误是一样的。

共有2个答案

贺景铄
2023-03-14

使用Control中的纯代码。函数。线性代替,以及系统中的IO。IO. Linear,因为前奏的内容根本没有声明为线性。

请注意,这个更简单的示例也不会编译:

sendToPeer :: Peer Idle %1-> IO (Peer Idle)
sendToPeer c = pure c

“withoutpure”版本的问题在我看来非常可疑,我认为这是一个bug,因为如果在参数级别进行模式匹配,它似乎会起作用:

sendToPeer :: Peer Idle %1-> Int -> Peer Busy
sendToPeer (Peer d) n = Peer d

解晟睿
2023-03-14

你遇到了多个问题:

  • 前奏曲。纯不是线性的。您需要使用控件中的线性Applicative类和相关方法函数pure。函子。线性线性基中

关于最后一点,GHC手册规定:

一个案例表达式可能会消耗它的scrutinee一次,或多次。但是这个推论仍然是实验性的,可能会过于急切地猜测它应该消耗scrutinee很多次。

线性基的用户指南更直截了当。它包括一个标题为Case statements not linear的部分,这表明你不能将它们用于线性函数。

现在,如果您想保留表达式的One特性,您必须避免检查带有case的表达式。

下面显示的是类型检查。我想我已经按照推荐的方式设置了进口。请注意,两种数据中都有pure的版本。函子。线性控制。函子。线性,它们的工作方式不同。有关数据,请参见文档顶部的注释。函子。线性模块。

{-# LANGUAGE LinearTypes #-}

module Lib where

import Prelude.Linear
import Control.Functor.Linear as Control
import qualified Prelude as NonLinear
import qualified System.IO.Linear as Linear

data Peer st = Peer String deriving Show

data Idle
data Busy

sendToPeer :: Peer Idle %1-> Int -> Linear.IO (Peer Busy)
sendToPeer (Peer d) n = Control.pure (Peer d)

 类似资料:
  • 我有以下结构: 项目中的setting.gradle如下所示: 现在我想在中使用。我可以在中使用而没有问题。为此,我将其添加到中的中: 但是,我无法导入相关类。建筑工程没有问题。当我使用的类时,Eclipse将相应的类导入到中的代码中,但是我得到的错误是无法解析导入。 现在解决这个问题的方法是将重命名为。然后一切都很好。但是,由于不是我的项目,所以我现在不能重命名它。有办法解决这个问题吗?

  • 我是Apache Beam的新手,并尝试使用DirectRunner和DataflowRunner运行示例读写程序。在我的用例中,CLI参数很少,为了实现这一点,我创建了一个扩展PipelineOptions的接口“CustomOptions.java”。 使用DirectRunner,程序运行良好,但使用DataflowRunner,它说“接口CustomOptions缺少一个名为‘项目’的属性

  • 本文向大家介绍请你回答一下为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数?相关面试题,主要包含被问及请你回答一下为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数?时的应答技巧和注意事项,需要的朋友参考一下 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。  

  • 在C#中,当我创建一个空类时,它提供了一个默认的构造函数,但是当我提供一个带有参数的构造函数时,默认的构造函数不再被创建。 我的问题是: 为什么编译器不再给我默认的构造函数呢 这些问题来自于WCF,我需要默认构造函数,但也希望能够为构造函数提供值,并且不必每次都放置默认构造函数,而且我不认为未使用的默认构造函数会使很多开销。

  • 本文向大家介绍为什么引入非线性激活函数?相关面试题,主要包含被问及为什么引入非线性激活函数?时的应答技巧和注意事项,需要的朋友参考一下 第一,对于神经网络来说,网络的每一层相当于f(wx+b)=f(w'x),对于线性函数,其实相当于f(x)=x,那么在线性激活函数下,每一层相当于用一个矩阵去乘以x,那么多层就是反复的用矩阵去乘以输入。根据矩阵的乘法法则,多个矩阵相乘得到一个大矩阵。所以线性激励函数

  • 问题内容: 我是Java编程语言的初学者,最近我研究了 构造函数 不能在Java中继承,有人可以解释 为什么 吗? 问题答案: 简而言之,构造函数不能被继承,因为在子类中它具有​​不同的名称(子类的名称)。 您只能执行以下操作: 相反,方法是使用“相同名称”继承的,可以使用。 理由如下:继承构造函数没有多大意义,因为类A的构造函数意味着创建类型A的对象,而类B的构造函数意味着创建类B的对象。 不过