箭头定律:首先仅取决于该对的第一个分量。为什么我们需要这个?

2023-12-19

约翰·休斯 (John Hughes) 在他的“将单子概括为箭头”中写道(第 8 章):

我们将财产形式化first f仅取决于对的第一个分量,如下所示:first f >>> arr fst = arr fst >>> f

据我了解,法律会过滤掉此类实施:

newtype KleisliMaybe a b = KMb { runKMb :: a -> Maybe b }

instance Category KleisliMaybe where 
 ...

instance Arrow KleisliMaybe where
 first f = KMb $ const Nothing
 ...

但对于这种情况,措辞似乎有点奇怪(我会选择“first没有副作用”或类似的情况)。

那么,还有哪些其他原因需要保留它呢?

此外,还有一条定律:first f >>> second (arr g) = second (arr g) >>> first f。我没有找到它过滤掉的任何实现(我做到了 - 查看编辑)。这部法律对我们有什么帮助?


编辑:对后一条法律的更多思考。

看看下面的代码片段:

newtype KleisliWriter = KW { runKW :: a -> (String, b) }

instance Category KleisliWriter where
 ...

instance Arrow KleisliWriter where
 arr f = KW $ \ x -> ("", f x)
 first  (KW f) = KW $ \ ~(a, d) -> f a >>= (\ x -> ("A", (x, d)))
 second (KW f) = KW $ \ ~(d, b) -> f b >>= (\ x -> ("B", (d, x)))

这样的实例的行为方式如下:

GHCi> c = KW $ \ x -> ("C", x)
GHCi> fst . runKW (first c >>> second (arr id)) $ (1, 2)
"CAB"
GHCi> fst . runKW (second (arr id) >>> first c) $ (1, 2)
"BCA"

据我所知,我们没有法律规定second f = swap >>> first f >>> swap。因此,我们可以禁止两者second and first该法律有任何副作用。然而,最初的措辞似乎仍然没有再次暗示这一点:

...我们形式化了直觉,即该对的第二个组成部分不受first f作为法律...

这些法律只是纯粹的形式化确凿证据吗?


简短回答:有一对不同的法律涵盖“first and second无副作用”:

first (arr f) = arr (first f)
second (arr f) = arr (second f)

想了想之后,我THINK您已经确定的两条法律:

first f >>> arr fst          =   arr fst >>> f                -- LAW-A
first f >>> second (arr g)   =   second (arr g) >>> first f   -- LAW-B

事实上,它们是多余的,因为它们遵循那些无副作用定律、其他定律和一些“自由定理”。

你的反例违反了无副作用法则,所以这就是为什么它们也违反了 LAW-A 和/或 LAW-B。如果有人有一个真正的反例,遵守无副作用定律,但违反了 LAW-A 或 LAW-B,我会很有兴趣看到它。

长答案:

该物业“first没有副作用(至少其本身没有副作用)”,该文第 8 条前面所述的法律更好地形式化了这一点:

first (arr f) = arr (first f)

回想一下,休斯说,如果箭头可以写,那么它就是“纯粹的”(相当于“没有副作用”)arr expr。因此,该定律指出,给定任何已经是纯粹的计算,因此可以写成arr f, 申请first该计算也会导致纯计算(因为它的形式是arr expr with expr = first f)。所以,first不引入杂质/本身不产生影响。

另外两条定律:

first f >>> arr fst          =   arr fst >>> f                -- LAW-A
first f >>> second (arr g)   =   second (arr g) >>> first f   -- LAW-B

旨在捕捉这样的想法:对于特定的instance Arrow Foo和一个特定的箭头动作f :: Foo B C, 那个行动:

first f :: forall d. Foo (B,d) (C,d)

作用于其输入/输出对的第一个组件,就好像第二个组件不存在一样。定律对应于以下性质:

  1. LAW-A:输出组件C并且任何副作用仅取决于输入B, 不输入d(即,不依赖于d)
  2. LAW-B:组件d不变地通过,不受输入影响B或任何副作用(即,对d)

对于 LAW-A,如果我们考虑行动first f :: Foo (B,d) (C,d)并重点关注C使用纯函数提取其输出的组成部分:

first f >>> arr fst :: Foo (B,d) C

那么结果与我们首先使用纯操作强制删除第二个组件相同:

arr fst :: Foo (B,d) B

并允许原来的动作f只采取行动B:

arr fst >>> f :: Foo (B,d) C

这里,结构first f >>> arr fst行动留下了这样的可能性:first f可以取决于d输入的组成部分在制定其副作用和构建C其输出的组成部分;但是,该结构的arr fst >>> f行动通过消除这种可能性d在允许任何不平凡的计算之前通过纯动作来组件f。这两个行为是平等的(法律)这一事实清楚地表明:first f产生一个C输出(和副作用,通过f, since first本身没有额外的效果)B以这样的方式输入can't还取决于d input.

LAW-B 更难。形式化该属性的最明显的方法是伪定律:

first f >>> arr snd = arr snd

这直接说明了first f不会改变提取的(arr snd) 第二个组成部分。然而,休斯指出,这是too限制性的,因为它不允许first f产生副作用(或至少任何可以在纯粹的行动中幸存下来的副作用)arr snd)。相反,他提供了更复杂的法律:

first f >>> second (arr g)   =   second (arr g) >>> first f

这里的想法是,如果first f曾经修改过d值,那么就会有some以下两个操作不同的情况:

-- `first f` changes `inval` to something else
second (arr (const inval)) >>> first f
-- if we change it back, we change the action
second (arr (const inval)) >>> first f >>> second (arr (const inval))

但是,由于 LAW-B,我们有:

second (arr (const inval)) >>> first f >>> second (arr (const inval))
-- associativity
= second (arr (const inval)) >>> (first f >>> second (arr (const inval)))
-- LAW-B
= second (arr (const inval)) >>> (second (arr (const inval)) >>> first f)
-- associativity
= (second (arr (const inval)) >>> (second (arr (const inval))) >>> first f
-- second and arr preserve composition
= second (arr (const inval >>> const inval)) >>> first f
-- properties of const function
= second (arr (const inval)) >>> first f

所以动作是相同的,与我们的假设相反。

然而,我猜想 LAW-A 和 LAW-B 都是多余的,因为我相信(见下面我的犹豫)它们遵循其他定律加上签名的“自由定理”:

first f :: forall d. Foo (B,d) (C,d)

假设first and second满足无副作用定律:

first (arr f) = arr (first f)
second (arr f) = arr (second f)

那么 LAW-B 可以重写为:

first f >>> second (arr g)              = second (arr g) >>> first f
-- no side effects for "second"
first f >>> arr (second g)              = arr (second g) >>> first f
-- definition of "second" for functions
= first f >>> arr (\(x,y) -> (x, g y))  = arr (\(x,y) -> (x, g y)) >>> first f

最后一个陈述只是自由定理first f。 (直觉上,因为first f是多态的类型d,任何纯粹的行动d必然是“看不见的”first f, so first f并且任何此类行为都会交换。)类似地,有一个自由定理:

first f >>> arr fst :: forall d. Foo (B,d) C

这抓住了这样一个想法,因为这个签名是多态的d,没有纯粹的预作用d可以影响动作:

arr (\(x,y) -> (x, g y)) >>> (first f >>> arr fst) = first f >>> arr fst

但左边可以重写:

-- by associativity
(arr (\(x,y) -> (x, g y)) >>> first f) >>> arr fst
-- by rewritten version of LAW-B
(first f >>> arr (\(x,y) -> (x, g y))) >>> arr fst
-- by associativity
first f >>> (arr (\(x,y) -> (x, g y)) >>> arr fst)
-- `arr` preserves composition
first f >>> arr ((\(x,y) -> (x, g y)) >>> fst)
-- properties of fst
first f >>> arr fst

给出右侧。

我只是在这里犹豫,因为我不习惯思考可能有效的箭头而不是函数的“自由定理”,所以我不能 100% 确定它会通过。

我很想知道是否有人能为这些违反 LAW-A 或 LAW-B 但满足无副作用定律的法律提出真正的反例。你的反例违反 LAW-A 和 LAW-B 的原因是它们违反了无副作用法则。对于你的第一个例子:

> runKMb (first (arr (2*))) (2,3)
Nothing
> runKMb (arr (first (2*))) (2,3)
Just (4,3)

对于你的第二个:

> runKW (first (arr (2*))) (1,2)
("A",(2,2))
> runKW (arr (first (2*))) (1,2)
("",(2,2))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

箭头定律:首先仅取决于该对的第一个分量。为什么我们需要这个? 的相关文章

  • 用parsec解析递归数据

    import Data Attoparsec Text Lazy import Data Text Lazy Internal Text import Data Text Lazy pack data List a Nil Cons a L
  • 如何使用 Haskell 中的 thyme 库从 Int 值创建 UTCTime?

    我有年 月 日 小时和分钟值 所有这些都是类型Int 我怎样才能将它们转换为UTCTime or UniversalTime 需要导入以下内容 import Control Lens import Data Thyme Clock impo
  • 如何在 Yesod 中使用 CSS 框架?

    我想将 Blueprint CSS 框架与 Yesod 一起使用 有没有最佳实践 因为 Yesod 使用 CSS 模板 所以在我看来我不能直接使用 css 文件 我必须将它们重命名为 lucius files 吗 如何将 CSS 添加到 d
  • ErrorT 已弃用,但 exceptT 不适合

    我有一个一元计算 在某些时候 由于单子模式匹配 它开始需要 MonadFail 约束 我的简单解决方法是使用以下命令运行它 fmap either error id runErrorT 然而哎呀 Deprecated Use Control
  • 使用 Promise 语法编写同步代码有什么好处吗?

    有同步承诺这样的概念吗 使用 Promise 语法编写同步代码有什么好处吗 try foo bar a b bam catch e handleError e 可以写成类似的东西 但使用同步版本then foo then bar bind
  • Haskell 中的前提条件检查有哪些选项

    这是一个简单的问题 我认为答案很复杂 一个非常常见的编程问题是函数返回某些内容 或者前置条件检查失败 在Java中 我会使用一些抛出异常的断言函数IllegalArgumentException在方法的开头 如下所示 method body
  • 在 Haskell 中计算移动平均线

    我正在学习 Haskell 所以我尝试实现移动平均函数 这是我的代码 mAverage Int gt Int gt Float mAverage x a fromIntegral k fromIntegral x k lt rawAvera
  • Haskell scala 互操作性

    我是 Scala 初学者 来自面向对象范式 在了解 Scala 的函数式编程部分时 我被引导到 Haskell 纯函数式编程语言 探索 SO 问题答案 我发现 Java Haskell 具有互操作性 我很想知道 Scala Haskell
  • 在 Haskell 中,为什么我必须在这段代码中使用美元符号?

    我仍在尝试破解这段代码 import Data Char groupsOf groupsOf n xs take n xs groupsOf n tail xs problem 8 x maximum map product groupsO
  • 在依赖类型的函数式编程语言中,扁平化列表是否更容易?

    在 haskell 中寻找一个可以展平任意深度嵌套列表的函数时 即应用的函数concat递归并在最后一次迭代时停止 使用非嵌套列表 我注意到这需要有一个更灵活的类型系统 因为随着列表深度的变化 输入类型也会变化 确实 有几个 stackov
  • Haskell 中的分类结构

    Hask通常被认为是一个范畴 其对象是类型 态射是函数 然而 我看到 Conor McBride pigworker 警告不要使用Hask多次 1 https stackoverflow com a 45905082 474311 2 ht
  • 使用 FoldLine 解析多个块

    对于这个简化的问题 我试图解析一个如下所示的输入 foo bar baz quux woo hoo xyzzy glulx into foo bar baz quux woo hoo xyzzy glulx 我尝试过的代码如下 import
  • Haskell 中列表列表的笛卡尔积

    给定一个长度列表的列表x所有子列表的长度都相同y 输出y x长度列表x包含每个子列表中的一项 例子 x 3 y 2 1 2 3 4 5 6 Output 2 3 8不同的输出 1 3 5 1 4 5 1 3 6 1 4 6 2 3 5 2
  • Haskell / GHC - 是否有“警告不完整模式”的中缀标签/编译指示

    我正在寻找一个可以对特定的不完整模式发出警告的编译指示 它会使编译器失败并显示以下 假设的 代码 FAILIF incomplete patterns f Int gt Int f 0 0 我正在尝试使用 Arrows 编写一个 编译器 并
  • 有没有更好的方法将 UTC 时间转换为大纪元时间?

    我想将文件的修改时间设置为从 exif 数据获取的时间 为了从 exif 获取时间 我发现 Graphics Exif getTag Exif gt String gt IO Maybe String 要设置文件修改时间 我发现 Syste
  • 为什么 ZipList 不是 List 的默认应用实例

    我目前正在学习 Haskell 中的应用程序 如果我没记错的话 列表有两个不同的应用实例 List and ZipList 第二个被定义为包装列表值的新类型 这ZipList应用实例对我来说似乎更直观 这可能是一个愚蠢的问题 但有具体原因吗
  • Haskell 对于 Web 应用程序来说足够成熟吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • Data.Sequence 中的 inits 和 tails 如何工作?

    Louis Wasserman 编写了当前的实现inits and tails in Data Sequence 他表示它们非常高效 事实上 只要查看代码 我就可以看到 无论它们在做什么 它们都是以干净 自上而下的方式进行的 这往往会给惰性
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采

随机推荐

  • iPhone 应用程序启动关闭的分配时间

    iPhone 应用程序 以及可能的其他重要例程 的启动和关闭需要花费多少时间 我的 iPhone 上的程序曾被过于热心的操作系统杀死吗 如果应用程序在 20 秒内没有响应 iPhone 上的看门狗计时器将终止您的应用程序 请注意 Xcode
  • 如何制作 iPython/Jupyter 中内联的 NLTK draw() 树

    对于 iPython Jupyter 中的 Matplotlib 绘图 您可以使笔记本绘图内联 matplotlib inline 如何对树的 NLTK draw 做同样的事情 这是文档http www nltk org api nltk
  • C# 中的多线程目录循环

    我试图循环遍历所有文件和文件夹 并对具有特定扩展名的所有文件执行操作 这种方法工作得很好 但我想使它成为多线程 因为当完成数万个文件时 它真的很慢 我会使用多线程进行成像会加快速度 我只是不确定在这种情况下如何使用线程 doStuff从文件
  • 加载时间:使用 PHP 的 DOMDocument 还是使用正则表达式解析 HTML 更快?

    我正在将图像从我的 Flickr 帐户提取到我的网站 并且我使用了大约九行代码来创建一个用于提取图像的 preg match all 函数 我已经读过好几次了 通过 DOM 解析 HTML 会更好 就我个人而言 我发现通过 DOM 解析 H
  • 向右移动菜单最后一项

    德尔福Xe2U4 主菜单项 文件 选项 帮助 名称 HelpMenuItem 2 个按钮 使用 StyleManager Xe2 在项目选项中启用 xe2 主题 并默认设置 Metro Blue Procedure TForm1 Right
  • 启用 Wi-Fi 时不接收移动状态状态更改事件

    当移动数据启用 禁用时我需要收到通知 为此 我使用 BroadcastReceiver 并注册到 ConnectivityManager CONNECTIVITY ACTION 事件 然而 只有当 Wi Fi 被禁用时才会触发该事件 一旦我
  • 按特定日期名称找出下周时间表的最佳方法

    想象我有一个名为NextSend代表DateTime Value 4 11 2011 10 30 00 AM Monday 假设我有一个必须每周在特定日期发送的时间表 Monday 在这种情况下 为了弄清楚下周的时间表 我最终得到了解决方案
  • 无法使用 Jena 写入大型 owl 文件

    我正在尝试将数据库表中包含的数据转换为一组三元组 因此我正在使用 Jena java 库编写一个 owl 文件 我已经成功地使用少量表记录 100 完成了此操作 这些记录对应于 owl 文件中的近 20 000 行 我对此感到满意 为了编写
  • IIS Windows 身份验证 401 未经授权

    我有一个想要使用 Windows 身份验证的子应用程序 我希望当用户第一次到达该页面时 即使在域中也会弹出登录框 当我关闭内核模式身份验证时 会弹出登录框 但在 3 次登录尝试后失败 并显示错误 401 未授权 如果我打开此功能 它甚至不会
  • ZF2 - 如果路由器匹配多个路由,将调度什么?

    那么 如果我有一个可能与许多路由匹配的 url 该怎么办 哪条路由会获胜 将调度哪个操作 是不是很简单 先定义 先调度 以下是路线示例 route catchall gt array type gt regex options gt arr
  • 作为 PySpark 的 reduceByKey 的键的列表

    我试图对格式的数据调用 pyspark 的 reduceByKey 函数 a b c 1 a b c 1 a d b e 1 看来 pyspark 不会接受数组作为普通键中的键 通过简单地应用 reduceByKey add 来减少值 我已
  • 如何在Java2D中旋转矩形

    我想在方法中旋转一个矩形 但不明白如何做到这一点并尝试如下 private void setBoundaryRotate Rectangle b int radio AffineTransform transform new AffineT
  • Chrome 不允许 HTTP 托管网站访问摄像头和麦克风

    我在用着反应网络摄像头 https github com mozmorris react webcam为应用程序拍摄自拍照 在本地主机上 react webcam 工作得很好 而在 HTTP 托管的网络服务器上 Chrome 上默认拒绝摄像
  • Python:我应该如何使实例变量可用?

    假设我有 class myclass def init self self foo bar 其中 foo 的值需要可供 myclass 的用户使用 直接从 myclass 的实例读取 foo 的值可以吗 我应该向 myclass 添加 ge
  • 打开 RDB 文件失败...只读文件系统

    我正在尝试在我的 redis 实例上执行保存或 bgsave 以运行备份 恢复过程 但是 当我尝试保存时出现错误 532 M 28 Jun 23 58 30 396 Failed opening the RDB file backup rd
  • 使用应用服务和 Azure SQL 的用户分配身份是否有效?

    我正在尝试让应用服务与 Azure Sql 数据库连接 我可以使用相同的代码很好地使用 git 处理系统分配的身份 但我更喜欢使用用户分配的身份 UAI 但我无法让它工作 我所做的步骤 通过门户创建了一个 UAI UAI 的名称为 uai
  • 重新映射 vim :x 来关闭缓冲区,仅当它是最后一个缓冲区时才退出

    I would like to change the x command in Vim so that it closes a buffer unless it is the last buffer then it should behav
  • 清单引用文件“Bing.Maps.dll”,该文件不是有效负载的一部分

    错误 1 清单引用文件 Bing Maps dll 该文件不是有效负载的一部分 C Users xxx Desktop xxx Applicationxx Applicationxx Package appxmanifest Applica
  • 什么是好的 Databricks 工作流程

    我使用 Azure Databricks 以及笔记本和管道进行数据处理 我对当前的工作流程不满意 在不中断生产的情况下 无法对生产中使用的笔记本进行修改 当我想要开发更新时 我会复制笔记本 更改源代码直到我满意为止 然后用新笔记本替换生产笔
  • 箭头定律:首先仅取决于该对的第一个分量。为什么我们需要这个?

    约翰 休斯 John Hughes 在他的 将单子概括为箭头 中写道 第 8 章 我们将财产形式化first f仅取决于对的第一个分量 如下所示 first f gt gt gt arr fst arr fst gt gt gt f 据我了