如何在没有冗余方法的情况下在Purescript中定义类型类实例

如何解决如何在没有冗余方法的情况下在Purescript中定义类型类实例

我正在尝试正确地自学Purescript。我目前正在研究Purescript book,并正在学习第6章。由于我对Haskell非常熟悉,所以到目前为止,练习非常容易。但是我目前还停留在其中,我当然知道“怎么做”,但似乎因有关定义类型类实例的Purescript特定行为而失败。这使我对在实践中如何做到这一点感到非常好奇。

我要做的具体事情是为Foldable类型定义一个NonEmpty实例,其定义如下:

data NonEmpty a = NonEmpty a (Array a)

从Haskell的背景出发,我知道foldMap往往是定义Foldable实例的最简单方法,所以我没有花时间写:

instance foldableNonEmpty :: Foldable NonEmpty where
    foldMap f (NonEmpty a as) = f a <> foldMap f as

这在Haskell中就足够了,因为所有其他Foldable方法在foldMap方面都有默认值。

我想在PureScript中也足够了,但是我的代码没有编译,给了我错误:

  The following type class members have not been implemented:
  foldr :: forall a b. (a -> b -> b) -> b -> ... -> b
  foldl :: forall a b. (b -> a -> b) -> b -> ... -> b

in type class instance

  Data.Foldable.Foldable NonEmpty

这让我感到惊讶,充其量似乎很不便。但是我检查了documentation,很快就发现确实有预定义的方法以foldl和{{的形式,从foldr中获取foldMapfoldlDefault 1}}。所以我的下一个尝试是:

foldrDefault

但这也无法编译。这次的错误是:

instance foldableNonEmpty :: Foldable NonEmpty where
    foldMap f (NonEmpty a as) = f a <> foldMap f as
    foldr = foldrDefault
    foldl = foldlDefault

这对我来说有点神秘,但是我认为这意味着我还不能访问 The value of foldableNonEmpty is undefined here,so this reference is not allowed. foldrDefault,因为(根据Pursuit文档)它们已经需要类型构造函数了有一个foldlDefault实例,因此不能用作定义该实例的一部分。

但是,所有这一切当然引出了一个问题:如何在Purescript中定义Foldable实例,而不必手动为3种方法中的2种编写多余的定义?与其他彼此定义方法的类型类相似。或者,如果有可能(我希望如此!),我想念的是什么?

实际上,在搜索Google后,我确实找到了一个Foldable实例的示例,并发现该实例确实可以编译:

Foldable

但是,这引出了一个新问题:据我所知,PureScript使用与Haskell完全相同的方式使用curring,为什么在上面的“ eta-reduced”版本中不允许这样做?以及与未定义值有关的错误消息与什么有关?

解决方法

是的,PureScript确实使用与Haskell完全相同的方式使用currying,但是在这里currying并不是问题。

问题是评估顺序。 Haskell使用正常的评估顺序,而PureScript使用可应用的评估顺序(简单来说,PureScript不是惰性的)。

这意味着Eta减少在PureScript和Haskell之间的工作方式不同(在某些情况下)。考虑以下示例:

f g x = if x > 0 then g x else 0
h x y = f (h x) y

如果我调用h 5 0,则结果为0,并且实际上从未递归调用h,因为在felse分支得到了求值。

在Haskell中,可以安全地减少Eta:

h x = f (h x)

但是,如果我在PureScript中编写了代码,则意味着对h x的每次调用都必须立即递归地调用h x才能将其结果传递给f,从而导致无限递归

在Haskell中这是有效的,因为在传递给h x之前,{em {em} 没有被评估,也就是“正常评估顺序”。


这与您的类实例类似,如果您还记得实例只是编译器会找出并为您透明传递的额外参数,并且实例声明可以看成是构造字典的函数(实际上就是这样)它已编译为JavaScript)。

要调用f,必须将实例传递给它,但这意味着递归调用字典构造函数,这将导致无限递归。

但是如果您是Eta扩展的,则实例字典在其自身的构造过程中不是必需的,只有在实际使用参数调用foldrDefault时才需要。


但是为什么世界上PureScript会使用可应用的评估顺序?!-您可能会愤慨地问。

好吧,我不是PureScript的设计师,所以我不能确切地回答这个问题,但是考虑到它已被编译为JavaScript,这对我来说确实有意义,并且使语义变得懒惰将意味着很多额外的麻烦。在编译后的代码中,这将使您难以在浏览器中进行检查和调试。


针对您的评论:

我在将其应用于实例定义时遇到了麻烦,特别是为什么为什么“如果您是Eta扩展的,则在其自身的构建过程中不需要实例字典”?仍然不需要该词典来确定foldrDefault到底是什么吗?

也许通过查看已编译的JavaScript会更容易说明。

对于减少Eta的情况,JavaScript如下所示:

foldr

对于以Eta扩展的情况,JavaScript如下:

var dictionary = {
    ...
    foldr: foldrDefault(dictionary)
    ...
}

可以看到,在前一种情况下,var dictionary = { ... foldr: function(y) { return foldrDefault(dictionary)(y) } ... } 的值在dictionary的初始化完成之前已传递给foldrDefault。 JavaScript实际上允许这样做,并且运行时行为是dictionary的参数最终将是foldrDefault,这当然会导致运行时崩溃。

这实际上是一段时间以来的编译器错误(对不起,我现在找不到GitHub问题),补丁是简单地禁止这种模式,只允许Eta扩展的情况,在这种情况下,您会看到,undefined的值只有在调用dictionary时才传递给foldrDefault,但不会在foldr初始化期间传递。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res