草庐IT

关于haskell:如何在scala中获得通用(多态)lambda?

codeneng 2023-03-28 原文

How to get generic (polymorphic) lambda in scala?

更新 (2018):我的祈祷在 Dotty (Type Lambdas) 中得到了回应,所以下面的 Q

  • @som-snytt Poly 似乎是一个很好的解决方法,所以我必须等待一个好的语法(才能真正看到 Poly 对象内的类型并具有 eta-expansion)。我的意思是 Shapeless 可以将 eta-expansion 定义为 Poly 以获得更好的语法。不管怎么说,还是要谢谢你。
  • @dk15 Scala 中的 Eta 扩展是方法和函数之间的一种笨拙的桥梁——它对 Shapelesss 多态函数值没有真正意义。
  • @Travis Brown - 我的意思是将方法扩展为 Poly 而不是 Function。所以我需要方便的桥接 Shapeless 的多功能而不是 scalas Function
  • @dk14 啊,明白了。 Poly(identity _) 等在某些情况下会起作用。
  • val a: (Int) => Int = f _; a(4) ?
  • @Travis Brown - 这就是我需要的 - 我曾经自己编写 Poly 函数(实现 Poly1 等) - 现在我的生活(在某些情况下)变得更轻松了。谢谢!


在 JVM/Scala 上只有方法可以是泛型的,而不是值。您可以创建一个实现某些接口的匿名实例(并为您要使用的每个类型重复它):

1
2
3
4
5
6
7
trait ~>[A[_], B[_]] { //exists in scalaz
  def apply[T](a: A[T]): B[T]
}

val f = new (List ~> Id) {
  def apply[T](a: List[T]) = a.head
}

或者使用 shapeless\\' Poly,它支持更复杂的类型案例。但是,是的,这是一个限制,需要解决。

  • 它实际上与 JVM 没有太大关系,因为它发生在编译时(也可以看到常规的 Function 解决方法)。我同意目前 Poly 似乎是最好的解决方案。


P?scal 是一个编译器插件,它提供了更简洁的语法,用于使用泛型方法将多态值编码为对象。

作为值的恒等函数具有类型?A. A => A。要将其转换为 Scala,请假设一个 trait

1
2
3
trait ForAll[F[_]] {
  def apply[A]: F[A]
}

然后恒等函数的类型为 ForAll[λ[A => A => A]],我使用 kind-projector 语法,或者,没有 kind-projector:

1
2
type IdFun[A] = A => A
type PolyId = ForAll[IdFun]

现在是 P?scal 语法糖:

1
val id = Λ[Α](a => a) : PolyId

或等效的

1
val id = ν[PolyId](a => a)

("ν"是希腊小写字母"Nu",读作"new")

这些实际上只是

的简写

1
2
3
new PolyId {
  def apply[A] = a => a
}

P?scal 支持多种类型参数和任意类型的参数,但您需要为每个变体在上述 ForAll 特征上进行专门的变体。

  • 这是非常好的!我提到过使用 kind-projector(类型级插件),我们(可能还没有尝试过)可以只写:type PolyId = ForAll[Lambda[A => A]] 甚至 val id = ν[ForAll[Lambda[A => A]]](a => a)。也许,您可以添加一些互操作性快捷方式(它可以在一个单独的项目中以使您的解决方案免受不必要的依赖)以及 val id = poly[A => A](a => a)
  • 对于 poly[A => A](a => a),我们需要确定并硬编码要实现的特征(此处为 ForAll)。所以插件必须附带一个库,其中包含 ForAll[F[_]] 上的许多变体,如 ForAll2[F[_, _]]ForAllH[F[_[_]]]ForAllHA[F[_[_]], G[_]]、... 另一种选择是使用结构类型,根据一些人的说法是一个非首发。
  • 我同意这种图书馆看起来"丑陋";但是,作为一个单独的(务实的)项目,为什么不呢?甚至可以说 shapeless 对 nat 有其限制,每个人都可以接受。此外,您可以使用宏生成这些类型(您可以为 Intellij IDEA 添加自定义扩展)。无论如何,对于一个平面参数,这个单线工作(Scala 2.12,鉴于 ForAll 已定义):val id = Λ[a](a => a) : ForAll[Lambda[a => a => a]],这真的很酷 :)
  • 也(仅适用于在此处阅读评论的人)val toList = Λ[a](x => List(x)) : ForAll[Lambda[a => a => List[a]]]toList.apply(5) - scala 出于某种原因强迫我显式调用 apply
  • 您至少需要一个显式 apply,因为它实际上是连续的两个 applytoList.apply[Int].apply(5)。您也可以将其写为 toList[Int](5)。当 ForAll[F] 中的 F[_] 是函数类型时,这种特殊情况可能具有一些额外的语法便利,但这取决于库。
  • >"但是,作为一个单独的(务实的)项目,为什么不呢?"与什么分开?它不能与插件分开。
  • 你可以在一个完全独立的项目中生成/硬编码 ForAll 的东西,因为它们根本不需要任何插件。快捷方式更复杂(也许这将是一个 hacky 解决方案),但它们仍然可以是一个单独的插件,将您的插件用作库(插件本质上是一个实现"插件"接口的 jar - 甚至种类 -投影机 KindRewriter 正式是一个公共类)


我真的很喜欢@Travis Brown 的解决方案:

1
2
3
4
import shapeless._

scala> Poly(identity _)
res2: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$1$2$@797aa352

-

1
2
3
4
5
scala> def f[T](x: T) = x
f: [T](x: T)T

scala> Poly(f _)
res3: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$2$2$@664ea816

-

1
2
3
4
5
6
7
8
9
10
11
scala> def f[T](l: List[T]) = l.head
f: [T](l: List[T])T

scala> val ff = Poly(f _)
ff: shapeless.PolyDefns.~>[List,shapeless.Id] = fresh$macro$3$2$@51254c50

scala> ff(List(1,2,3))
res5: shapeless.Id[Int] = 1

scala> ff(List("1","2","3"))
res6: shapeless.Id[String] = 1

Poly 构造函数(在某些情况下)会给你 eta-expansion 到 Shapeless2 Poly1 函数,这是(更多)真正通用的。但是它不适用于多参数(即使使用多类型参数),因此必须使用 implicit at 方法"实现" Poly2 (如@som-snytt 建议的那样),类似于:

1
2
3
4
5
6
7
8
9
object myF extends Poly2 {
  implicit def caseA[T, U] = at[T, U]{ (a, b) => a -> b}
}

scala> myF(1,2)
res15: (Int, Int) = (1,2)

scala> myF("a",2)
res16: (String, Int) = (a,2)

附言我真的很想把它看作是语言的一部分。

  • 我想我们都会,但在这个阶段它可能在 Scala 中发生的变化太大了。你可能会看例如Idris 是一种从 Scala 中汲取灵感的语言,但对类型级编程有更多支持。


看起来你需要做一些类型提示来帮助 Scala 类型推断系统。

1
def id[T] : T => T = identity _

所以我想如果您尝试将身份作为参数传递给函数调用并且该参数的类型是通用的,那么应该没有问题。

  • 它仍然不是一个值 - 无法以通用方式将其传递给另一个函数。简单地说,你在这里定义了一个方法——而不是一个函数
  • 请参阅 stackoverflow.com/questions/2529184/
  • 我的意思是 id _ 仍然是 Nothing => Nothing。我可以将其用作别名的解决方法,但不能用作通用解决方案。
  • 看看:stackoverflow.com/questions/15264838/
  • 好的,我的问题有点重复,但是我正在寻找该问题的任何通用解决方案 - 因为现在我们有宏和无形 - 也许有人创建了可以做到这一点的库。

有关关于haskell:如何在scala中获得通用(多态)lambda?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  6. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  7. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  8. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  9. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  10. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

随机推荐