在Scala中使用foldLeft将参数列表应用于curried函数

在Scala中使用foldLeft将参数列表应用于curried函数,scala,currying,hlist,Scala,Currying,Hlist,是否可以对参数列表执行foldLeft,其中提供给fold的初始值是一个完全通用的函数,运算符是apply,列表是要传递给函数f的参数列表 例如,假设f定义为: scala> val f = (i: Int, j: Int, k: Int, l: Int) => i+j+k+l f: (Int, Int, Int, Int) => Int = <function4> 或者一次一个地用咖喱和应用参数: scala> f.curried res2: Int =&

是否可以对参数列表执行
foldLeft
,其中提供给fold的初始值是一个完全通用的函数,运算符是
apply
,列表是要传递给函数
f
的参数列表

例如,假设f定义为:

scala> val f = (i: Int, j: Int, k: Int, l: Int) => i+j+k+l
f: (Int, Int, Int, Int) => Int = <function4>
或者一次一个地用咖喱和应用参数:

scala> f.curried
res2: Int => Int => Int => Int => Int = <function1>

scala> f.curried.apply(1).apply(2).apply(3).apply(4)
res3: Int = 10
但是,这会产生以下错误:

<console>:9: error: type mismatch;
 found   : Int => Int => Int => Int
 required: Int => Int => Int => Int => Int
              List(1, 2, 3, 4).foldLeft(f.curried)({ (g, x) => g.apply(x) })
我的第一个想法是联合类型在这里会很有用。我看过迈尔斯·萨宾用柯里·霍华德推导的联合类型,所以如果第一个预感是真的,那么我似乎拥有解决问题所需的基本机制

然而:即使联合类型是答案,如果我可以引用“所有类型的联合,从函数的完全curried类型到curried函数的类型,除了最后一个参数外,其他所有参数都提供”,这将是有用的。换言之,转换类型的方法:

T1 => ... => Tn
进入联合类型:

(T1 => ... => Tn) |∨| ... |∨| (Tn-1 => Tn)
作为上述
g
的类型很有用

列表上执行
foldLeft
将讨论限制在
T1
Tn-1
都相同的情况下。类似于

(T1 =>)+ Tn
将描述我要为
g
提供的类型

我要问的具体情况不需要任意长的链,因此我们可以使用

(T1 =>){1,4} Tn
展望未来,我们希望对不相等的类型链执行此操作,但是,在类型上使用一些神奇的功能,将链分割成所有后缀集,可能更有用:

Suffixes(T1 => ... => Tn)
实现这一点目前远远超出了我的Scala能力。任何关于如何进行的提示都将不胜感激。我不知道这是否可以通过高级使用Scala现有的类型系统或者通过编译器插件来实现,或者两者都不能

正如在下面的评论中所指出的,将结果称为“联合类型”并不完全适合此用例。我不知道还能叫它什么,但这是我目前最接近的想法。其他语言对这个想法有特别的支持吗?这在Coq和Agda中如何工作


对我来说,命名这个问题并理解它在更大范围内的位置(类型理论、可判定性等)比使用
ANSWER
的工作实现更重要,尽管两者都很好。任何能与Scalaz、幺半群或范畴理论建立联系的人都可以获得加分。

好的,没有Scalaz,没有解决方案,只有解释。如果在REPL中使用f.curried.apply和1,然后使用2个参数,那么每次返回的结果类型实际上都不同!FoldLeft很简单。它的类型是固定的,你的起始参数是f.curried,因为它没有与f.curried相同的签名。apply(1)它不起作用。
因此,起始参数和结果必须是同一类型。foldLeft的起始和元素的类型必须一致。你的结果是Int,所以这绝对不起作用。希望这能有所帮助。

您的函数正好需要4个
Int
参数
foldLeft
是一个应用于任意数量元素的函数。您提到了
List(1,2,3,4)
,但如果您有
List(1,2,3,4,5)
List()
,该怎么办

List.foldLeft[B]
也希望函数返回相同的类型
B
,但在您的情况下
Int
和一些
Function1[Int,
不是相同的类型

你提出的任何解决方案也不会是一般性的。例如,如果函数的类型为
(Int,Float,Int,String)=>Int
,该怎么办?然后需要一个
列表[任何]

因此,这绝对不是
List.foldLeft
的工作

记住这一点(警告非常不符合scala代码):


这比我最初预期的要简单得多

首先,我们需要定义一个简单的
HList

sealed trait HList

final case class HCons[H, T <: HList](head : H, tail : T) extends HList {
  def ::[H1](h : H1) = HCons(h, this)
  override def toString = head+" :: "+tail.toString
}

trait HNil extends HList {
  def ::[H1](h : H1) = HCons(h, this)
  override def toString = "HNil"
}

case object HNil extends HNil
type ::[H, T <: HList] = HCons[H, T]
我们也可以对具有不同arity和非统一参数类型的函数使用相同的未修改的
foldCurry

val f2 = (i : Int, s : String, d : Double) => (i+1, s.length, d*2)
val f2c = f2.curried

val l2 = 23 :: "foo" :: 2.0 :: HNil

// In the REPL ... again, note the inferred result type
scala> foldCurry(l2, f2c)
res1: (Int, Int, Double) = (24,3,4.0)

我想我的问题可能会归结为“Scala支持并集类型吗?”如果是的话,是否有语法上的甜点来指代“所有类型的并集,从一个函数的完全curry类型到f类型,除了最后一个参数之外,都提供了”?在这种情况下直接使用Sabin的表示法会很麻烦。如果有一种方法可以将“T1=>…=>Tn”类型转换为联合(T1=>…=>Tn)v…v(Tn-1=>Tn)”类型,并使用一些特殊的操作员,这在这里会非常有用。我的手机阻止我邮寄tat链接:/但是你也可以随时用谷歌搜索它。使用scala,关于这个主题的材料还不多。感谢Andreas和@ArjanBlokzijl的评论。我很好奇是否有可能像我之前的评论中提到的那样扩展Sabin的联合类型。(现在包含在原始帖子的更新中)我不确定联合类型是否适合这里。看起来你想要的是curried函数的折叠式,而不是用HList表示的参数。我认为这样的事情可能是可行的。无论如何,这是一个有趣的问题。。。如果我能想出任何可行的办法,我会调查并正确回答。哇——谢谢,迈尔斯。我很想听听你对这个问题的看法。我同意工会类型本身似乎并不完全符合这种情况的要求。@MilesSabin我已经澄清了我的问题,并提出了一些可能的答案形式。我仍然不清楚这是否是或应该是可能的,但不管怎样,这对我来说都是一个很好的练习,可以让我把它讲清楚。谢谢你,@huynjl。我已经澄清了我的问题,说明我正在寻找一个除了为
g
添加一个类型之外,不会修改原始表达式的答案,
Suffixes(T1 => ... => Tn)
class Acc[T](f: Function1[T, _]) {
  private[this] var ff: Any = f
  def apply(t: T): this.type = {
    ff = ff.asInstanceOf[Function1[T,_]](t)
    this
  }
  def get = ff match { 
    case _: Function1[_,_] => sys.error("not enough arguments")
    case res => res.asInstanceOf[T]
  }
}

List(1,2,3,4).foldLeft(new Acc(f.curried))((acc, i) => acc(i)).get
// res10: Int = 10
sealed trait HList

final case class HCons[H, T <: HList](head : H, tail : T) extends HList {
  def ::[H1](h : H1) = HCons(h, this)
  override def toString = head+" :: "+tail.toString
}

trait HNil extends HList {
  def ::[H1](h : H1) = HCons(h, this)
  override def toString = "HNil"
}

case object HNil extends HNil
type ::[H, T <: HList] = HCons[H, T]
trait FoldCurry[L <: HList, F, Out] {
  def apply(l : L, f : F) : Out
}

// Base case for HLists of length one
implicit def foldCurry1[H, Out] = new FoldCurry[H :: HNil, H => Out, Out] {
  def apply(l : H :: HNil, f : H => Out) = f(l.head)
}

// Case for HLists of length n+1
implicit def foldCurry2[H, T <: HList, FT, Out]
  (implicit fct : FoldCurry[T, FT, Out]) = new FoldCurry[H :: T, H => FT, Out] {
    def apply(l : H :: T, f : H => FT) = fct(l.tail, f(l.head))
}

// Public interface ... implemented in terms of type class and instances above
def foldCurry[L <: HList, F, Out](l : L, f : F)
  (implicit fc : FoldCurry[L, F, Out]) : Out = fc(l, f)
val f1 = (i : Int, j : Int, k : Int, l : Int) => i+j+k+l
val f1c = f1.curried

val l1 = 1 :: 2 :: 3 :: 4 :: HNil

// In the REPL ... note the inferred result type
scala> foldCurry(l1, f1c)
res0: Int = 10
val f2 = (i : Int, s : String, d : Double) => (i+1, s.length, d*2)
val f2c = f2.curried

val l2 = 23 :: "foo" :: 2.0 :: HNil

// In the REPL ... again, note the inferred result type
scala> foldCurry(l2, f2c)
res1: (Int, Int, Double) = (24,3,4.0)