代码之家  ›  专栏  ›  技术社区  ›  Alois Cochard

Scala可以被看作是一个“抽象反转”吗?

  •  3
  • Alois Cochard  · 技术社区  · 14 年前

    问候语,

    这是一个具有挑衅性的问题,旨在就如何在开发人员社区中看到抽象倒置展开辩论。我很想知道你是怎么想的。

    首先,这里引用了维基百科给出的抽象反转示例: http://en.wikipedia.org/wiki/Abstraction_inversion

    创建一个对象来表示一个函数在面向对象语言(如Java和C++)中是繁琐的,其中函数不是一流对象。在C++中,通过重载()操作符可以使对象“可调用”,但仍然需要实现一个新的类,例如STL中的函子。

    对我来说,函数在Scala中是一流的公民,但是当我们使用Scala生成Java字节码时,Scala会在Java的“顶部”创建特定的类,使函数编程成为可能。。。 我们能把这看作是一个抽象的倒置吗?

    这同样适用于Clojure或JVM的任何函数语言。。。甚至是Apache集合,例如:

    http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Closure.html


    顺便说一句,我不相信维基百科的文章是客观的。例如,当谈到微内核中可能的抽象反转时,文章说 “有人认为微内核设计是一种抽象的倒置” 但是在OOP中没有这样的函数类型语句

    4 回复  |  直到 14 年前
        1
  •  9
  •   Daniel C. Sobral    14 年前

    wiki文章真的很弱(它本身是否代表了一个抽象反转?:),这个概念本身就有点可疑。但它的基本要点似乎是抽象隐藏了一些基本元素,迫使抽象的用户重新实现它。

    例如,在talk页面上,出现了一个更有趣的例子。假设一个CPU有一个 tan 数学函数,但不是 sin cos ,并实现了一种编程语言 余弦 依据 棕褐色的 ,但没有曝光 棕褐色的 本身。使用这种语言的程序员将被迫实现 棕褐色的 依据 余弦 ,它们本身的实现方式是 棕褐色的 从而刻画了抽象反演的特征。

    所以,回到斯卡拉。当然,在Scala中使用函数的程序员不会产生抽象反转,因为他不会被迫重新实现Scala的基本功能。

    另一方面,可以说Scala将函数实现为类实例是抽象反转的实例。然而,要做到这一点,必须有两件事是正确的:

    1. JVM必须有一个可用的“函数”原语。
    2. 这样一个原语一定为Scala提供了另一种选择。

    什么是函数?函数原语是什么样子的?在此上下文中,“函数”是指能够执行的数据。有人可能会说,实际上,所有汇编程序代码都是能够执行的数据——只是它不可移植,而且,更进一步说,它不是字节码,因此不符合设计原则。

    另一方面,Java中的所有方法都被一个标识符引用,Java通过这个标识符定位要为给定对象的类层次结构执行的代码。虽然可以通过反射间接使用此标识符,但它不会公开。如果它是公开的,并且提供了一些功能来表示“调用此代码”,那么可以说可以围绕此构造一个函数。

    所以,我想一个案例可以证明1是真的。我们继续下一个。

    如果Java 如果提供“method”数据类型,Scala函数是否不再是类的实例?不,他们不会。Scala的一个基本设计方面是,运行程序的每个元素都是一个对象。Java已经拥有的“原语”就好像它们是带有方法的普通对象一样,如果有一个“方法”原语,它也会这样。

    方法原语的一个可能结果是将方法提升为一等公民,但函数本身几乎不会改变。

        2
  •  5
  •   Kevin Wright    14 年前

    通过一个对象实现一个函数并不仅仅因为这是JVM上可能实现的,它直接切入Scala的基本原理。

    一切 是一个对象:函数、参与者、数字文本等。任何对象也可以应用(通过定义 apply() 方法)而不是FunctionN的子类。

    这种二元性是许多标准库api设计的基础,它允许将映射同时视为函数(将键映射到值)和对象(键/值对的集合)。

    Scala是一个真正的对象/功能混合体,两种范式都不占主导地位。

        3
  •  2
  •   Landei    14 年前

    不,它不能被看作是抽象倒置。原因很简单:在Scala中 选择 您喜欢哪个抽象级别。大多数时候写作更方便

    val f = 2 * (_:Int)
    //--> f: (Int) => Int = 
    
    f(21)
    //--> res5: Int = 42
    

    但要走“长”路也没问题:

    val f = new Function1[Int,Int] { def apply(v:Int) = 2*v }
    //--> f: java.lang.Object with (Int) => Int = 
    
    f(21)
    //--> res6: Int = 42
    

    由于FunctionN是特性,所以可以在继承中使用混合,这使得您可以避免类似于C++中的函子的情况。E、 g.您可以将Java地图“改装”为一个函数:

    class JavaMap[K,V] extends java.util.HashMap[K,V] with Function1[K,V] {
        def apply(k:K) = get(k)
    }
    
    val m = new JavaMap[Int, String]
    m.put(5,"five")
    m(5)
    //--> res8: String = five
    
        4
  •  1
  •   Artyom Shalkhakov    13 年前

    我不相信。在Scala中实现一级函数的方法就是,一个实现细节。