Haskell:instance Functor ((->) r) 的意义是什么

2024-02-19

我承认,我的问题可能源于缺乏知识,而且比较模糊。 但我试着去理解,有一些疑问,却无法解决。

那么GHC.Base有这样的定义,它的意义是什么:

instance Functor ((->) r) where
    fmap = (.)
  1. 从编程语言的角度来看: 我们有真正的基础构造(->),我认为比任何东西都更基础,但也许是术语,并且您将其描述为非常衍生的构造(实例函子)的一部分。有什么意义呢? (->) 是 (->)。函子对于 Haskell 下描述的 (->) 有意义。但反之则不然:(->) 有意义,而 Functor 在 Haskell 库中描述正确。

  2. 从lambda演算的角度来看: 2.1 如果根据“常识”定义“(->) r”是 r 周围的容器(我们称之为“Any_f”),那么函数 fmap 应该如何工作? fmap 应该将值更改为容器,但不要更改容器结构,尝试写入它。

    fmap f (Any_f x) Any_f (f x)

(是的,这是非类型化 lambda 演算)

2.2.但让我们看看 Haskell 中如何定义 Functor ((->) r) :

instance Functor ((->) r) where
    fmap = (.)
    -- Or in other words (quotation marks intentionaly):
    -- fmap f (Any_f x) = f (Any_f x) 
    -- fmap :: forall a, b, c => (b -> c) -> (a -> b) -> (a -> c)

So:

  • “常识”(不改变容器结构)告诉我们这样写:

    fmap f (Any_f_as_container x) = Any_f_as_container (f x)

  • 类型要求告诉我们这样写:

    fmap f (any_f_as_container x) = f (Any_f_as_container x)

这不是说“instance Functor ((->) r)”毫无意义吗?如果不是——当它改变最外层的函数(容器本身,而不是容器值)时,它有什么意义?


我会尽力让你相信fmap = (.)实际上是使容器的形状保持不变,但将函数应用于容器中的所有元素。但在我们这样做之前(->),让我们对一些更简单的类型进行处理。具体来说,让我们对具有特定数量元素的容器类型进行此操作 - 即,恰好具有两个元素的容器将是TwoF,而没有元素的一个将是ZeroF。像这样:

data ZeroF  a = ZeroF
data OneF   a = OneF   a
data TwoF   a = TwoF   a a
data ThreeF a = ThreeF a a a

应该做什么Functor这些实例看起来像什么?嗯,那个是为了OneF looks exactly就像你的问题一样:

instance Functor OneF where
    fmap f (OneF x) = OneF (f x)

其他的看起来pretty类似——只是申请f更多(或更少)次来解释存在更多(或更少)元素的事实。它们都在这里,有一些创造性的空白来突出相似性/模式:

instance Functor ZeroF  where fmap f (ZeroF          ) = ZeroF
instance Functor OneF   where fmap f (OneF   x0      ) = OneF   (f x0)
instance Functor TwoF   where fmap f (TwoF   x0 x1   ) = TwoF   (f x0) (f x1)
instance Functor ThreeF where fmap f (ThreeF x0 x1 x2) = ThreeF (f x0) (f x1) (f x2)

希望现在你同意这绝对有味道Functor您在问题中描述的实例:保持容器的形状相同,并应用给定的函数f到其中包含的每个元素。

这些是具有给定数量元素的容器。现在,让我们为这些容器编写访问器——即我们想要相当于(!!)对于列表,如果给定一个数字,我们从容器中取出该字段。由于 a 中有零个元素ZeroF,我们需要一个零值的索引类型;同时为ThreeF我们需要一个具有三个值的索引类型。

data Zero
data One   = One0
data Two   = Two0   | Two1
data Three = Three0 | Three1 | Three2

索引函数的类型如下所示:

indexZero  :: ZeroF  a -> Zero  -> a
indexOne   :: OneF   a -> One   -> a
indexTwo   :: TwoF   a -> Two   -> a
indexThree :: ThreeF a -> Three -> a

我不会全部实现它们——它们非常简单——但这里有一个可以给你一个想法,以防它不是很明显。

indexTwo (TwoF x0 x1) Two0 = x0
indexTwo (TwoF x0 x1) Two1 = x1

事实证明,索引函数有一个逆函数——如果你给我一个函数,当给定索引时,它会产生一个值,那么我可以给你一个包含这些值的容器。类型如下所示:

tabulateZero  :: (Zero  -> a) -> ZeroF  a
tabulateOne   :: (One   -> a) -> OneF   a
tabulateTwo   :: (Two   -> a) -> TwoF   a
tabulateThree :: (Three -> a) -> ThreeF a

(你明白为什么这是逆矩阵的正确类型吗?请注意,比如说,TwoF a -> Two -> a与以下类型相同TwoF a -> (Two -> a)!)只是为了让您了解它们是如何实现的(如果不是很明显),我们只需将索引函数应用于每个索引:

tabulateZero  ix = ZeroF
tabulateOne   ix = OneF   (ix One0  )
tabulateTwo   ix = TwoF   (ix Two0  ) (ix Two1  )
tabulateThree ix = ThreeF (ix Three0) (ix Three1) (ix Three2)

证明这一点并不难tabulateX . indexX = id and indexX . tabulateX = id对于每个X,即制表实际上是索引的逆过程。

好的,但现在请稍等一下,看看我们刚刚做了什么:我们已经变成了function (like Two -> a) 变成容器 (like TwoF a),反之亦然。类型Two -> a and TwoF a从道德上来说,完全相同的事情。所以认为我们可以实施似乎是合理的fmap for Two -> a-- 例如,只需转换为TwoF a并酌情返回!

twomap :: (a -> b) -> (Two -> a) -> (Two -> b)
twomap f = indexTwo . fmap f . tabulateTwo

让我们想象一下它在做什么。我们将从任意索引函数开始:

\case Two0 -> x0; Two1 -> x1

现在我们来回顾一下整个过程:

               \case Two0 -> x0; Two1 -> x1
tabulateTwo
               TwoF x0 x1
fmap f
               TwoF (f x0) (f x1)
indexTwo
               \case Two0 -> f x0; Two1 -> f x1

Since f应用于两个分支,我们可以将其从case:

               f . (\case Two0 -> x0; Two1 -> x1)

第二项正是我们开始使用的索引函数。换句话说,我们刚刚确定了另一种更简单的实现twomap:

twomap f ix = f . ix

如果你通过类似的推理来解决zeromap, onemap, and threemap,你会发现它们实际上都有相同的实现!我们可以通过多态来对所有不同大小的容器统一执行此操作;而不是有onemap为了改变One -> a等,让我们有一个xmap为了改变x -> a's:

xmap :: (a -> b) -> (x -> a) -> (x -> b)
xmap f ix = f . ix

当然,我们不必说出名字f and ix:

xmap = (.)

...这就是Functor实例为(x -> _).

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

Haskell:instance Functor ((->) r) 的意义是什么 的相关文章

  • 类 GADT 类型变量的未来角色?

    A 昨天的问题 https stackoverflow com q 41135212 3072788有一个定义HList 来自HList https hackage haskell org package HList 0 4 1 0 doc
  • 如何让 Show 显示函数名称?

    作为一个让我熟悉 Haskell 的简单练习 在 Youtube 上闲逛并偶然进入美国倒计时游戏节目之后 我想为数字游戏制作一个求解器 你得到 6 个数字 需要将它们与 为了得到给定的结果 到目前为止我所得到的是非常脑死亡的 let ope
  • 如何从 haskell 中的 IOError 获取 errno?

    我在 haskell 平台上 GHC 6 12 1 作为 apt get 安装在 Debian Squeeze 上 鉴于我需要在与最初引发它的线程不同的线程上使用它 如何从 IOError 中获取底层 errno 我需要这个的原因是因为我正
  • 在 Haskell 中计算移动平均线

    我正在学习 Haskell 所以我尝试实现移动平均函数 这是我的代码 mAverage Int gt Int gt Float mAverage x a fromIntegral k fromIntegral x k lt rawAvera
  • Haskell 类型系统的细微差别

    我一直在深入了解 haskell 类型系统的本质 并试图了解类型类的要点 我已经学到了很多东西 但我在下面的代码片段上遇到了困难 使用这些类和实例定义 class Show a gt C a where f Int gt a instanc
  • 将数据类型设置为 Kind * -> * 这不是函子

    布伦特 约尔吉类型分类百科全书 https www haskell org haskellwiki Typeclassopedia给出以下练习 举一个类型的例子 gt 不能将其制成 的实例Functor 不使用undefined 请告诉我什
  • 我应该在 Turtle 或 Foldl 包中使用折叠吗?

    我在使用 Turtle 时遇到了一些困难 直到盯着难以理解的错误消息几分钟后才意识到我使用了错误的fold功能 https hackage haskell org package turtle 1 5 8 docs Turtle Shell
  • 如何在 Haskell 中向右或向左移动列表的 1 个元素?

    嗨 我一直在寻找答案 但找不到 假设我们有一个像这样的列表 1 10 4 5 3 我怎样才能将 5 向左移动 使这个列表变成 1 10 5 4 3 我尝试过了swapElementsAt通过找到该元素的索引 但它看起来非常不足 swapEl
  • 在依赖类型的函数式编程语言中,扁平化列表是否更容易?

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

    我正在尝试为不同的函数实现一个容器类 我可以在其中保存函数指针并稍后用它来调用这些函数 我会尝试更准确地描述我的问题 例如 我有两个不同的测试函数 int func1 int a int b printf func1 works i i n
  • 如何在 Haskell 中安装库?

    我尝试使用控制 Monad Extra andM https hackage haskell org package extra 1 7 10 docs Control Monad Extra html import Control Mon
  • 使用 FoldLine 解析多个块

    对于这个简化的问题 我试图解析一个如下所示的输入 foo bar baz quux woo hoo xyzzy glulx into foo bar baz quux woo hoo xyzzy glulx 我尝试过的代码如下 import
  • 从成员函数指针类型生成函子

    我正在尝试简化 通过make fn 预处理参数的函子的生成 通过wrap 对于 arity 的成员函数n 生成函子基本上可以工作 但到目前为止只能通过显式指定成员函数的参数类型来实现 现在我想从它处理的成员函数类型生成正确的函子 struc
  • 在 Yesod 生态系统中,对某些文本进行 urlencode 的最佳方式是什么?

    我想对一些文本进行 url 编码 例如 用 20 替换每个空格等 我找到了 HTTP Network HTTP Base urlEncode 并且可以使用它 但我想知道是否还有其他通常在 Yesod 生态系统中使用的东西 不幸的是 由于 U
  • Haskell / GHC - 是否有“警告不完整模式”的中缀标签/编译指示

    我正在寻找一个可以对特定的不完整模式发出警告的编译指示 它会使编译器失败并显示以下 假设的 代码 FAILIF incomplete patterns f Int gt Int f 0 0 我正在尝试使用 Arrows 编写一个 编译器 并
  • Haskell 中的尾递归字符串分割

    我正在考虑分割字符串的问题s在一个字符处c 这表示为 break c s 其中 Haskell 库定义break c 足够接近 br br s h t if c h then s else let h t br t in h h t 假设我
  • QuickCheck是否可以生成任意函数

    我试图为身份编写一个 QuickCheck 测试 f y f y 我最初的计划是编写一个返回函数和整数的任意生成器 具有签名Gen Int gt Int Int 并在prop DollerDoesNothing使用 不使用测试该功能应用程序
  • 如何在haskell中获取变量名称

    我来到 haskell 时有一些 c 背景知识 想知道是否有类似的 define print a printf s d n a a int a 5 print a 应该打印 a 5 这是 augustss 提到的 TH 解决方案 LANGU
  • 你能识别 Haskell 程序中的无限列表吗? [复制]

    这个问题在这里已经有答案了 可能的重复 如何判断列表是否是无限的 https stackoverflow com questions 7371730 how to tell if a list is infinite 在Haskell中 你
  • Haskell 对于 Web 应用程序来说足够成熟吗? [关闭]

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

随机推荐

  • 禁用 CAST AS 以优化实体框架中的查询

    我在用着实体框架5我想从中选择数据甲骨文10g数据库 问题是数据库表很大并且生成的查询实体框架是无效的 我想摆脱那些CAST column AS type 有什么设置可以关闭它们吗 C code var context new APPDB
  • 正则表达式可以是数字字符,也可以是字母数字但不仅仅是字母字符

    要验证必须同时包含数字和字母的字段 我可以使用 0 9 a zA Z a zA Z 0 9 0 9a zA Z 但我想另外接受仅数字字符串 你的意思是你至少想要一个号码 您可以使用前瞻断言 d a z d i
  • Pandas 获取映射函数中元素的索引

    我正在使用 pandas 来分析到不同节点的现有 ssh 会话 因为我已经解析了 ssh 守护进程日志 并且我有一个包含以下列的 DataFrame Node 建立连接的节点名称 会话 会话ID Start 指示连接何时开始的时间戳 Fin
  • 我应该如何为最终的 64 位编译器准备 32 位 Delphi 程序? [复制]

    这个问题在这里已经有答案了 可能的重复 迁移到 Delphi 2010 和 Unicode 时如何为 64 位做好准备 https stackoverflow com questions 1568685 how to also prepar
  • 从java调用带有表值参数的存储过程

    在我的应用程序中 我想执行类似的查询SELECT FROM 表 WHERE col IN list 其中 list 可以有可变的值 我正在使用 MS SQL 服务器数据库 当我用谷歌搜索这个问题时 我发现了这个链接 http www som
  • 如何检查 Kafka 中的消费者是否消费了某个主题

    如何检查特定消费者群体是否完全消费了某个主题 这相当于在标准排队系统中检查队列是否为空 这并不明显 因为每个消费者只能看到给定的分区 因此它不知道其组中的其他消费者是否消耗了他们的部分 我想这应该以某种方式完成AdminUtils or Z
  • 如何使div相对于父级的高度为100%?

    我坚持下面这样的事情 我需要将右上 div 设为 100 高度 其背景颜色将覆盖主 div 的整个高度 div style width 100 margin auto text align left border 1px solid 628
  • Magento 2 标签翻译显示大括号

    显示双花括号之间的多个翻译的标签 我在 Magento 2 的前端和后端都遇到这个问题 这是我到目前为止所尝试过的 清除并刷新缓存 重新部署静态内容 将 Magento 2 2 5 更新至 2 2 6 请查看图片以获取更多信息 Magent
  • 如何获取 JavaFX 中某个阶段的关闭事件?

    在JavaFX中 如果用户单击阶段的关闭按钮 X 最右上方的十字 如何获取事件 我希望我的应用程序在窗口关闭时打印调试消息 System out println Application Close by click to Close But
  • 为什么表别名通常都是小写?

    我总是这样看例子 但为什么呢 这是一个好的做法吗 因此它们与查询的其余部分 通常以大写形式编写 不同 至于这是否是最佳实践 如果您以全部大写形式编写查询 那么它肯定会使您的查询更易于阅读和理解
  • 如果代码中没有 return 语句,函数在 C 程序中返回什么

    我做了一个关于如何在 C 中递归地反转单链表的函数 功能如下 struct node reverseSLL2 struct node p struct node temp struct node temp1 if p NULL temp1
  • NEST 查询精确文本匹配

    我正在尝试编写一个 NEST 查询 该查询应根据精确的字符串匹配返回结果 我在网上研究过 有关于使用术语 匹配 匹配短语的建议 我已经尝试了所有这些 但我的搜索返回的结果包含部分搜索字符串 例如 在我的数据库中 我有以下几行电子邮件地址 电
  • 如何实施xgboost增量训练?

    问题是由于列车数据大小 我的列车数据无法放入 RAM 中 所以我需要一种方法 首先在整个训练数据集上构建一棵树 计算残差构建另一棵树等等 就像梯度提升树一样 显然如果我打电话model xgb train param batch dtrai
  • Rails 选择 GROUP 中 COUNT 最高的对象

    目标是选择Store其中一个Coupon最常用于 目前 我有这个 并且它有效 分解以供解释 coupon rb has many redemptions has and belongs to many stores def most pop
  • SML 和函数式编码风格

    我开始学习标准机器学习编程语言 https www coursera org course proglang course 在第一个作业中 我尝试编写一个函数is older需要两个日期并评估为true or false 它评估为true如
  • 使用 JavaScript 将 CSV 转换为 XML

    我是 javascript 新手 我需要将 csv 字符串数据转换为 xml 如下所示 CSV 数据 product pakageing qty mno 100ML 200 pqr 400ML 300 abc 150ML 100 XML 应
  • 使用 chromecast 从 IP 摄像机进行流式传输

    我正在尝试通过我的 Android 设备通过 IP 摄像头进行流传输 我已经从发布的代码中编辑了 URLGitHub https github com googlecast cast android sample当我尝试从摄像机的 IP 地
  • log4net 用于 IIS 中托管的 WCF 服务库

    对于一个项目 我有一个 WCF 服务库 目前非常简单 它通过 WCF 服务网站项目托管在 IIS 7 5 中 对于该 WCF 服务库 我需要 log4net 来记录一些重大事件 但启动并访问网站后 不会创建任何日志文件 这是我的配置详细信息
  • 返回 std::map 值的地址是否安全?

    我有一个std map
  • Haskell:instance Functor ((->) r) 的意义是什么

    我承认 我的问题可能源于缺乏知识 而且比较模糊 但我试着去理解 有一些疑问 却无法解决 那么GHC Base有这样的定义 它的意义是什么 instance Functor gt r where fmap 从编程语言的角度来看 我们有真正的基