精华内容
下载资源
问答
  • scala学习trait

    千次阅读 2016-06-01 22:37:18
    Trait英文意思是特质和性状),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来实现各种应用程序的...

    Scala里相当于Java接口的是Trait(特征)。Trait的英文意思是特质和性状),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来实现各种应用程序的功能要求,Scala也是按照这个构想来设计的。

    一般情况下Scala的类只能够继承单一父类,但是如果是特征的话就可以继承多个,从结果来看就是实现了多重继承。


    trait Action{
    def doAction
    }
    trait TBeforeAfter extends Action{
    abstract override def doAction{
    println("initialization")
    super.doAction
    println("Destory")
    }
    }
    class Work extends Action{
    override def doAction=println("working...")
    }


    class Human{
    println("Human")
    }
    trait TTeacher extends Human{
    println("TTeacher")
    def teach
    }
    trait PianoPlayer extends Human{
    println("PianoPlayer")
    def playPiao={
    println("I am playing piano.")
    }
    }


    class PianoTeacher extends Human with TTeacher with PianoPlayer{
    override def teach={
    println("I am training students.")
    }
    }




    object AopTest{
    def main(args:Array[String]){


    val work=new Work with TBeforeAfter
    work.doAction
    //initialization
    //working...
    //Destory


    val t1=new PianoTeacher
    t1.playPiao
    t1.teach
    //Human
    //TTeacher
    //PianoPlayer
    //I am playing piano.
    //I am training students.


    val t2=new Human with TTeacher with PianoPlayer{
    def teach={println("I am teaching students")}
    }
    t2.playPiao
    t2.teach
    //Human
    //TTeacher
    //PianoPlayer
    //I am playing piano.
    //I am teaching students.
    }
    }



    class AbstractClassOps{
    var id:Int=_
    //val id:Int=_    //错误
    }
    abstract class SuperTeacher(val name:String){
    var id:Int
    var age:Int
    def teach
    }
    class TeacherForMaths(name:String) extends SuperTeacher(name){
    override var id=name.hashCode()
    override var age=22
    override def teach{
    println("Teaching!!")
    }
    }
    object AbstractClassOps{
    def main(args:Array[String]){
    val teacher=new TeacherForMaths("spark")
    teacher.teach


    println(teacher.id)
    println(teacher.name)
    }
    }

    展开全文
  • trait在Scala英文翻译为特征,是一种类似于Java中的interface,但又区别于interface。主要区别在于:trait可以定义自己的属性和实现方法体,而interface则不能自己实现方法体。从类的角度来说,它又有点类似于抽象类...

    1、trait是什么?

    trait在Scala英文翻译为特征,是一种类似于Java中的interface,但又区别于interface。主要区别在于:trait可以定义自己的属性和实现方法体,而interface则不能自己实现方法体。从类的角度来说,它又有点类似于抽象类,因为trait就是把一些共同的性质抽象抽来(属性、方法),哪些类需要就继承它,并且有所进化的可以进行覆写(override).

    2、举例说明

    package com.dtspark.scala.basic
    trait Logger{
      def log(message:String){
      println("Logger: "+ message)
      }
    }
    
    trait RichLogger extends Logger{
      override def log(message:String){
      println("RichLogger: "+ message)
      }
    }
    
    class Loggin(val name:String) extends Logger{
      def loggin{
        println("Hi,welcome "+ name)
        log(name)
      }
    }
    
    trait Information{
      def getInformation:String
      def checkIn:Boolean={
        getInformation.equals("Spark")
      }
    }
    class Passenger(val name:String) extends Information{
      def getInformation=name
    }
    
    
    object HelloTrait {
      def main(args:Array[String]):Unit={
        val personLoggin=new Loggin("DTSpark") with RichLogger
        personLoggin.loggin
      }
    }

    代码说明:
    1、 val personLoggin=new Loggin(“DTSpark”) with RichLogger 实现多继承需要用with连接。
    2、代码运行结果
    Hi,welcome DTSpark
    RichLogger: DTSpark

    3、使用trait需要注意的

    super关键字是调用原来的方法(trait里头的)
    例:

    trait SchoolTrait {    
       def study()  
    }  
    
    trait ClassTrait extends SchoolTrait{    
      abstract override def study()={  
        //上课前向老师问好  
        println("=====Hello Teacher=====")  
          //调用原来的处理  
          super.study  
        println("=====Goodbye Teacher=====")  
        //下课后向老师道别  
      }    
    }  

    测试代码:

    class TeachClass extends SchoolTrait{    
      def study(){  
        println("*** start math cource ***");  
      }     
    }    
    object TeachClass {  
      def main(agrs : Array[String]){  
        val test = new TeachClass with ClassTrait  
        test.study  
      }    
    }  

    结果:
    =====Hello Teacher=====
    * start math cource *
    =====Goodbye Teacher=====

    展开全文
  • 本文原发于我的个人博客:https://hltj.me/kotlin/2020/01/11/kotlin-trait-typeclass.html。本副本只用于知乎,禁止第三方转载。trait 与类型类都是什么trait 与类型类(type class)分别是...值得一提的是虽然英文...

    89aea78519b801b354048f5d798b3b22.png

    本文原发于我的个人博客:https://hltj.me/kotlin/2020/01/11/kotlin-trait-typeclass.html。本副本只用于知乎,禁止第三方转载。

    trait 与类型类都是什么

    trait类型类(type class)分别是 Rust 与 Haskell 语言中的概念,用于特设多态(ad-hoc polymorphism)、函数式编程等方面。

    值得一提的是虽然英文都是“trait”, Scala 的特质跟 Rust 的 trait[^注1] 却并不相同。 Scala 的特质相当于 Kotlin 与 Java 8+ 的接口,能实现子类型多态;而 Rust 的 trait 更类似于 Swift 的协议与 Haskell 的类型类,能实现特设多态。简单来说,trait 应同时具备以下三项能力[^注2]:

    1. 定义“接口”并可提供默认实现
    2. 用作泛型约束
    3. 给既有类型增加功能

    Haskell 的类型类不仅同时具备这三项能力,还能定义函数式编程中非常重要的 Functor、Applicative、Monad 等。 当然这是废话,因为它们在 Haskell 中本来就是类型类 。 实际上这也不是 trait 与类型类的差异,能否支持 Functor 等的关键在于语言的泛型参数能否支持类型构造器(或者说语言能否支持高阶类型)。

    [^注1]:trait:Scala 中文社区倾向于译为“特质”,Rust 中文社区倾向于不译。

    [^注2]:按说“接口”不支持默认实现也可实现 Rust/Haskell 式的特设多态,但其易用性与表现力都要大打折扣。

    在 Kotlin 中寻求对应

    在 Kotlin 中并没有同时具备这三项能力的对应,只有分别提供三项能力的特性。 其中 Kotlin 的接口同时具备前两项能力。

    定义“接口”并可提供默认实现

    例如,定义一个带有默认实现的接口:

    interface WithDescription {
        val description: String get() = "The description of $this"
    }

    Kotlin 的接口中可以定义属性与方法,二者都可以有默认实现,简便起见,示例中用了具有默认实现的属性。它可以这么用:

    class Foo: WithDescription {
        // Foo 类为 description 属性提供了自己的实现
        override val description = "This is a Foo object"
    }
    
    // 对象 Bar 的 description 属性采用默认实现
    object Bar: WithDescription
    
    println(Foo().description)
    println(Bar.description)

    在Kotlin REPL中执行会得到类似这样的输出:

    This is a Foo object
    The description of Line_7$Bar@5bf4764d

    用作泛型约束

    接下来还可以将之前定义的 WithDescription 接口用在泛型函数、泛型类或者其他泛型接口中作为泛型约束,例如:

    fun <T : WithDescription> T.printDescription() = println(description)

    在 REPL 中执行:

    >>> Bar.printDescription()
    The description of Line_7$Bar@5bf4764d

    遗憾的是,在 Kotlin 中不能给既有类型(类或接口)实现新的接口,比如不能为Boolean或者Iterable实现WithDescription。 即接口不具备第三项能力,因此它不是 trait/类型类。

    给既有类型增加功能

    在 Kotlin 中给既有类型增加功能的方式是扩展,可以给任何既有类型声明扩展函数与扩展属性。例如可以分别给 IntString 实现二者间的乘法操作符函数:

    operator fun Int.times(s: String) = s.repeat(this)
    
    operator fun String.times(n: Int) = repeat(n)

    于是就可以像 Python/Ruby 那样使用了:

    >>> "Hello" * 3
    res11: kotlin.String = HelloHelloHello
    >>> 5 * "汉字"
    res12: kotlin.String = 汉字汉字汉字汉字汉字

    在 Kotlin 中“实现”trait/类型类

    如上文所述,Kotlin 分别用接口与扩展两个不同特性提供了 trait/类型类的三项能力,因此在 Kotlin 中没有其直接对应。 那么如果把两个特性以某种方式结合起来,是不是就可以“实现”trait/类型类了?——还别说,真就可以! Arrow 中的类型类就是这么实现的。

    我们继续以 WithDescription 为例,不同的是,这回要这么声明:

    interface WithDescription<T> {
        val T.description get() = "The description of $this"
    }

    这里利用了分发接收者可以子类化、扩展接收者静态解析的特性,可以为任何既有类型添加实现。例如分别为CharString实现如下:

    object CharWithDescription : WithDescription<Char> {
        override val Char.description get() = "${this.category} $this"
    }
    
    // 采用默认实现
    object StringWithDescription: WithDescription<String>

    不过使用时会麻烦一点,需要借助run()或者with()这样的作用域函数在相应上下文中执行:

    println(StringWithDescription.run { "hello".description })
    
    with(CharWithDescription) {
        println('a'.description)
    }

    在 REPL 中执行的输出如下:

    The description of hello
    LOWERCASE_LETTER a

    用作泛型约束也不成问题:

    fun <T, Ctx : WithDescription<T>> Ctx.printDescription(t: T) = println(t.description)
    
    StringWithDescription.run {
        CharWithDescription.run {
            printDescription("Kotlin")
            printDescription('①')
        }
    }

    这里实现的printDescription()与上文的函数签名不同,因为接收者类型用于实现基于作用域上下文的泛型约束了,这也是利用接口、扩展、子类型多态以及作用域函数这些特性来“实现”trait/类型类的关键所在。 当然,如果仍然希望目标类型(如例中的CharString)作为printDescription的接收者,只要将其接收者与参数互换即可:

    fun <T, Ctx : WithDescription<T>> T.printDescription(ctx: Ctx) = ctx.run {
        println(description)
    }
    
    "hltj.me".printDescription(StringWithDescription)

    上述两种方式中提供泛型约束的上下文要么占用了函数的扩展接收者、要么占用了函数参数。实际上还有一种方式——占用分发接收者,显然只要在WithDescription内声明printDescription()就可以了。 不过我们这里要假设printDescription()是自己定义的函数,而WithDescription是无法修改的既有类型,那么还能做到吗?——当然不成问题!只要用一个新接口继承WithDescription就可以了:

    interface WithDescriptionAndItsPrinter<T>: WithDescription<T> {
        fun T.printDescription() = println(description)
    }
    
    object StringWithDescriptionAndItsPrinter: WithDescriptionAndItsPrinter<String>
    
    object CharWithDescriptionAndItsPrinter:
        WithDescriptionAndItsPrinter<Char>, WithDescription<Char> by CharWithDescription
    
    StringWithDescriptionAndItsPrinter.run {
       CharWithDescriptionAndItsPrinter.run {
            "hltj.me".printDescription()
            '★'.printDescription()
        }
    }

    将三种方式放一起对比会更直观:

    // 方式 1
    StringWithDescription.run {
        printDescription("hltj.me")
    }
    
    // 方式 2
    "hltj.me".printDescription(StringWithDescription)
    
    // 方式 3
    interface WithDescriptionAndItsPrinter { /*……*/ }
    object StringWithDescriptionAndItsPrinter: WithDescriptionAndItsPrinter<String>
    StringWithDescriptionAndItsPrinter.run {
        "hltj.me".printDescription()
    }

    第三种方式的优点是提供泛型约束的上下文既不占用扩展接收者也不占用参数,但其代价是需要为每个用到的目标类型(如例中的CharString)提供新接口(如例中的WithDescriptionAndItsPrinter<T>)的相应实现,并且依然需要借助作用域函数run()with()。 因此通常采用前两种方式即可,但是如果要自定义操作符函数或者中缀函数时就只能采用第三种方式了,例如:

    interface DescriptionMultiplier<T>: WithDescription<T> {
        infix fun T.rep(n: Int) = (1..n).joinToString { description }
    
        operator fun T.times(n: Int) = this rep n
    }
    
    object CharDescriptionMultiplier:
        DescriptionMultiplier<Char>, WithDescription<Char> by CharWithDescription
    
    println(object : DescriptionMultiplier<String> {}.run { "hltj.me" rep 2 })
    
    println(CharDescriptionMultiplier.run { 'A' * 3 })

    在 REPL 中执行的输出为:

    The description of hltj.me, The description of hltj.me
    UPPERCASE_LETTER A, UPPERCASE_LETTER A, UPPERCASE_LETTER A

    扩展与成员的优先级

    我们知道,在 Kotlin 中扩展与成员冲突时总是取成员。 但是在使用基于作用域上下文的泛型约束时却并非如此,例如:

    interface WithLength<T> {
        val T.length: Int
    }
    
    object StringWithFakeLength: WithLength<String> {
        override val String.length get() = 128
    }
    
    fun <T, U: WithLength<T>> U.printLength(t: T) = println(t.length)
    
    StringWithFakeLength.run {
        printLength("hltj.me")
    }

    在 REPL 中运行输出是128,表明printLenth()取到的lengthStringWithFakeLength中定义的扩展属性而不是String自身的属性。因此使用时需要特别注意。唯有 Any 的三个成员toString()hashCode()equals()会始终调用成员函数,即便在泛型约束上下文中声明了具有相同签名的扩展函数也是一样。

    “实现”Functor 等

    按照上文介绍的方式,我们可以轻松实现 ShowEqOrd 等简单类型类,无需赘述。 但是如果要实现 FunctorApplicativeMonad 等却会遇到问题。 以 Functor 为例,按说要这么定义:

    interface Functor<C<*>> {
        fun <T, R> C<T>.fmap(f: (T) -> R): C<T>
    }

    但遗憾的是上述代码无法通过编译,因为 Kotlin 目前不支持高阶类型,在泛型参数中用C<*>表示类型构造器只是假想的语法 。 因此,需要有一种方式来变通。按Arrow 的方式引入Kind接口来表示:

    interface Kind<out F, out A>
    
    interface Functor<F> {
        fun <T, R> Kind<F, T>.fmap(f: (T) -> R): Kind<F, R>
    }

    然后写一个标记类,让具体类型作为Kind<标记类, T>的实现类。再定义一个由Kind<标记类, T>向具体类型转换的扩展函数fix(),以便在具体实现中使用。 例如:

    class ForMaybe private constructor()
    
    sealed class Maybe<out T> : Kind<ForMaybe, T> {
        object `Nothing#` : Maybe<Nothing>() {
            override fun toString(): String = "Nothing#"
        }
        data class Just<out T>(val value: T) : Maybe<T>()
    }
    
    fun <T> Kind<ForMaybe, T>.fix(): Maybe<T> = this as Maybe<T>

    这样就可以为Maybe实现Functor<ForMaybe>了:

    object MaybeFunctor : Functor<ForMaybe> {
        override fun <T, R> Kind<ForMaybe, T>.fmap(f: (T) -> R): Maybe<R> = when (val maybe = fix()) {
            is Maybe.Just -> Maybe.Just(f(maybe.value))
            else -> Maybe.`Nothing#`
        }
    }
    
    fun main() = with(MaybeFunctor) {
        println(Maybe.Just(5).fmap { it + 1 })
        println(Maybe.`Nothing#`.fmap { x: Int -> x + 1 })
    }

    可以看出这种实现方式会有明显的局限性:只能为 Arrow 中定义的类型或者按照 Arrow 方式实现的既有类型实现 FunctorApplicativeMonad 等接受类型构造器作为泛型参数的“类型类”。 好在 Arrow 已经自带了大量有用的类型,很多场景都够用。

    需要注意的是这段代码无法在当前版本(1.3.61)的 Kotlin REPL 中运行,需要放在普通的 Kotlin 文件中编译运行。

    Arrow

    Arrow(按其官网写作 Λrrow)是 Kotlin 标准库的函数式“伴侣”。目前主要以下四套件:

    • Arrow Core 提供了核心的数据类型与类型类。
    • Arrow FX 是函数式副作用库,提供了 do-表示法/Monad 推导风格的 DSL。
    • Arrow Optics 用于在 Kotlin 中处理不可变数据模型。
    • Arrow Meta 是 Kotlin 编译器与 IDE 的函数式“伴侣”。

    此外还有若干套件/特性还在孵化中。 关于 Arrow 整体与模式的介绍也在 Arrow Core 的文档中,其中 Functional Programming Glossary 提供了一些使用 Arrow 进行函数式编程的背景知识可供参考。

    一点意外

    在尝试写这些示例时意外发现了一个会导致当前版本的 Kotlin JVM 编译器抛异常的 bug,最小重现代码如下:

    interface WithIntId<T> {
        val T.intId get() = 1
    }
    
    object BooleanWithIntId : WithIntId<Boolean>
    
    val x = BooleanWithIntId.run {
        true.intId
    }

    只影响 Kotlin JVM 编译器,Kotlin JS 与 Kotlin Native 都不存在这个问题。 查了下 YouTrack,看起来是个已知 bug。 不过文中的其他示例代码都能正常编译运行,尽可放心。

    展开全文
  • 本文原发于我的个人博客:https://hltj.me/kotlin/2020/01/11/kotlin-trait-typeclass.html。本副本只用于知乎,禁止第三方转载。trait 与类型类都是什么trait 与类型类(type class)分别是...值得一提的是虽然英文...

    3dfcfb1eb9bc6c68769f5ebab6b4ca6e.png

    本文原发于我的个人博客:https://hltj.me/kotlin/2020/01/11/kotlin-trait-typeclass.html。本副本只用于知乎,禁止第三方转载。

    trait 与类型类都是什么

    trait类型类(type class)分别是 Rust 与 Haskell 语言中的概念,用于特设多态(ad-hoc polymorphism)、函数式编程等方面。

    值得一提的是虽然英文都是“trait”, Scala 的特质跟 Rust 的 trait[^注1] 却并不相同。 Scala 的特质相当于 Kotlin 与 Java 8+ 的接口,能实现子类型多态;而 Rust 的 trait 更类似于 Swift 的协议与 Haskell 的类型类,能实现特设多态。简单来说,trait 应同时具备以下三项能力[^注2]:

    1. 定义“接口”并可提供默认实现
    2. 用作泛型约束
    3. 给既有类型增加功能

    Haskell 的类型类不仅同时具备这三项能力,还能定义函数式编程中非常重要的 Functor、Applicative、Monad 等。 当然这是废话,因为它们在 Haskell 中本来就是类型类 。 实际上这也不是 trait 与类型类的差异,能否支持 Functor 等的关键在于语言的泛型参数能否支持类型构造器(或者说语言能否支持高阶类型)。

    [^注1]:trait:Scala 中文社区倾向于译为“特质”,Rust 中文社区倾向于不译。

    [^注2]:按说“接口”不支持默认实现也可实现 Rust/Haskell 式的特设多态,但其易用性与表现力都要大打折扣。

    在 Kotlin 中寻求对应

    在 Kotlin 中并没有同时具备这三项能力的对应,只有分别提供三项能力的特性。 其中 Kotlin 的接口同时具备前两项能力。

    定义“接口”并可提供默认实现

    例如,定义一个带有默认实现的接口:

    interface WithDescription {
        val description: String get() = "The description of $this"
    }

    Kotlin 的接口中可以定义属性与方法,二者都可以有默认实现,简便起见,示例中用了具有默认实现的属性。它可以这么用:

    class Foo: WithDescription {
        // Foo 类为 description 属性提供了自己的实现
        override val description = "This is a Foo object"
    }
    
    // 对象 Bar 的 description 属性采用默认实现
    object Bar: WithDescription
    
    println(Foo().description)
    println(Bar.description)

    在Kotlin REPL中执行会得到类似这样的输出:

    This is a Foo object
    The description of Line_7$Bar@5bf4764d

    用作泛型约束

    接下来还可以将之前定义的 WithDescription 接口用在泛型函数、泛型类或者其他泛型接口中作为泛型约束,例如:

    fun <T : WithDescription> T.printDescription() = println(description)

    在 REPL 中执行:

    >>> Bar.printDescription()
    The description of Line_7$Bar@5bf4764d

    遗憾的是,在 Kotlin 中不能给既有类型(类或接口)实现新的接口,比如不能为Boolean或者Iterable实现WithDescription。 即接口不具备第三项能力,因此它不是 trait/类型类。

    给既有类型增加功能

    在 Kotlin 中给既有类型增加功能的方式是扩展,可以给任何既有类型声明扩展函数与扩展属性。例如可以分别给 IntString 实现二者间的乘法操作符函数:

    operator fun Int.times(s: String) = s.repeat(this)
    
    operator fun String.times(n: Int) = repeat(n)

    于是就可以像 Python/Ruby 那样使用了:

    >>> "Hello" * 3
    res11: kotlin.String = HelloHelloHello
    >>> 5 * "汉字"
    res12: kotlin.String = 汉字汉字汉字汉字汉字

    在 Kotlin 中“实现”trait/类型类

    如上文所述,Kotlin 分别用接口与扩展两个不同特性提供了 trait/类型类的三项能力,因此在 Kotlin 中没有其直接对应。 那么如果把两个特性以某种方式结合起来,是不是就可以“实现”trait/类型类了?——还别说,真就可以! Arrow 中的类型类就是这么实现的。

    我们继续以 WithDescription 为例,不同的是,这回要这么声明:

    interface WithDescription<T> {
        val T.description get() = "The description of $this"
    }

    这里利用了分发接收者可以子类化、扩展接收者静态解析的特性,可以为任何既有类型添加实现。例如分别为CharString实现如下:

    object CharWithDescription : WithDescription<Char> {
        override val Char.description get() = "${this.category} $this"
    }
    
    // 采用默认实现
    object StringWithDescription: WithDescription<String>

    不过使用时会麻烦一点,需要借助run()或者with()这样的作用域函数在相应上下文中执行:

    println(StringWithDescription.run { "hello".description })
    
    with(CharWithDescription) {
        println('a'.description)
    }

    在 REPL 中执行的输出如下:

    The description of hello
    LOWERCASE_LETTER a

    用作泛型约束也不成问题:

    fun <T, Ctx : WithDescription<T>> Ctx.printDescription(t: T) = println(t.description)
    
    StringWithDescription.run {
        CharWithDescription.run {
            printDescription("Kotlin")
            printDescription('①')
        }
    }

    这里实现的printDescription()与上文的函数签名不同,因为接收者类型用于实现基于作用域上下文的泛型约束了,这也是利用接口、扩展、子类型多态以及作用域函数这些特性来“实现”trait/类型类的关键所在。 当然,如果仍然希望目标类型(如例中的CharString)作为printDescription的接收者,只要将其接收者与参数互换即可:

    fun <T, Ctx : WithDescription<T>> T.printDescription(ctx: Ctx) = ctx.run {
        println(description)
    }
    
    "hltj.me".printDescription(StringWithDescription)

    上述两种方式中提供泛型约束的上下文要么占用了函数的扩展接收者、要么占用了函数参数。实际上还有一种方式——占用分发接收者,显然只要在WithDescription内声明printDescription()就可以了。 不过我们这里要假设printDescription()是自己定义的函数,而WithDescription是无法修改的既有类型,那么还能做到吗?——当然不成问题!只要用一个新接口继承WithDescription就可以了:

    interface WithDescriptionAndItsPrinter<T>: WithDescription<T> {
        fun T.printDescription() = println(description)
    }
    
    object StringWithDescriptionAndItsPrinter: WithDescriptionAndItsPrinter<String>
    
    object CharWithDescriptionAndItsPrinter:
        WithDescriptionAndItsPrinter<Char>, WithDescription<Char> by CharWithDescription
    
    StringWithDescriptionAndItsPrinter.run {
       CharWithDescriptionAndItsPrinter.run {
            "hltj.me".printDescription()
            '★'.printDescription()
        }
    }

    将三种方式放一起对比会更直观:

    // 方式 1
    StringWithDescription.run {
        printDescription("hltj.me")
    }
    
    // 方式 2
    "hltj.me".printDescription(StringWithDescription)
    
    // 方式 3
    interface WithDescriptionAndItsPrinter { /*……*/ }
    object StringWithDescriptionAndItsPrinter: WithDescriptionAndItsPrinter<String>
    StringWithDescriptionAndItsPrinter.run {
        "hltj.me".printDescription()
    }

    第三种方式的优点是提供泛型约束的上下文既不占用扩展接收者也不占用参数,但其代价是需要为每个用到的目标类型(如例中的CharString)提供新接口(如例中的WithDescriptionAndItsPrinter<T>)的相应实现,并且依然需要借助作用域函数run()with()。 因此通常采用前两种方式即可,但是如果要自定义操作符函数或者中缀函数时就只能采用第三种方式了,例如:

    interface DescriptionMultiplier<T>: WithDescription<T> {
        infix fun T.rep(n: Int) = (1..n).joinToString { description }
    
        operator fun T.times(n: Int) = this rep n
    }
    
    object CharDescriptionMultiplier:
        DescriptionMultiplier<Char>, WithDescription<Char> by CharWithDescription
    
    println(object : DescriptionMultiplier<String> {}.run { "hltj.me" rep 2 })
    
    println(CharDescriptionMultiplier.run { 'A' * 3 })

    在 REPL 中执行的输出为:

    The description of hltj.me, The description of hltj.me
    UPPERCASE_LETTER A, UPPERCASE_LETTER A, UPPERCASE_LETTER A

    扩展与成员的优先级

    我们知道,在 Kotlin 中扩展与成员冲突时总是取成员。 但是在使用基于作用域上下文的泛型约束时却并非如此,例如:

    interface WithLength<T> {
        val T.length: Int
    }
    
    object StringWithFakeLength: WithLength<String> {
        override val String.length get() = 128
    }
    
    fun <T, U: WithLength<T>> U.printLength(t: T) = println(t.length)
    
    StringWithFakeLength.run {
        printLength("hltj.me")
    }

    在 REPL 中运行输出是128,表明printLenth()取到的lengthStringWithFakeLength中定义的扩展属性而不是String自身的属性。因此使用时需要特别注意。唯有 Any 的三个成员toString()hashCode()equals()会始终调用成员函数,即便在泛型约束上下文中声明了具有相同签名的扩展函数也是一样。

    “实现”Functor 等

    按照上文介绍的方式,我们可以轻松实现 ShowEqOrd 等简单类型类,无需赘述。 但是如果要实现 FunctorApplicativeMonad 等却会遇到问题。 以 Functor 为例,按说要这么定义:

    interface Functor<C<*>> {
        fun <T, R> C<T>.fmap(f: (T) -> R): C<T>
    }

    但遗憾的是上述代码无法通过编译,因为 Kotlin 目前不支持高阶类型,在泛型参数中用C<*>表示类型构造器只是假想的语法 。 因此,需要有一种方式来变通。按Arrow 的方式引入Kind接口来表示:

    interface Kind<out F, out A>
    
    interface Functor<F> {
        fun <T, R> Kind<F, T>.fmap(f: (T) -> R): Kind<F, R>
    }

    然后写一个标记类,让具体类型作为Kind<标记类, T>的实现类。再定义一个由Kind<标记类, T>向具体类型转换的扩展函数fix(),以便在具体实现中使用。 例如:

    class ForMaybe private constructor()
    
    sealed class Maybe<out T> : Kind<ForMaybe, T> {
        object `Nothing#` : Maybe<Nothing>() {
            override fun toString(): String = "Nothing#"
        }
        data class Just<out T>(val value: T) : Maybe<T>()
    }
    
    fun <T> Kind<ForMaybe, T>.fix(): Maybe<T> = this as Maybe<T>

    这样就可以为Maybe实现Functor<ForMaybe>了:

    object MaybeFunctor : Functor<ForMaybe> {
        override fun <T, R> Kind<ForMaybe, T>.fmap(f: (T) -> R): Maybe<R> = when (val maybe = fix()) {
            is Maybe.Just -> Maybe.Just(f(maybe.value))
            else -> Maybe.`Nothing#`
        }
    }
    
    fun main() = with(MaybeFunctor) {
        println(Maybe.Just(5).fmap { it + 1 })
        println(Maybe.`Nothing#`.fmap { x: Int -> x + 1 })
    }

    可以看出这种实现方式会有明显的局限性:只能为 Arrow 中定义的类型或者按照 Arrow 方式实现的既有类型实现 FunctorApplicativeMonad 等接受类型构造器作为泛型参数的“类型类”。 好在 Arrow 已经自带了大量有用的类型,很多场景都够用。

    需要注意的是这段代码无法在当前版本(1.3.61)的 Kotlin REPL 中运行,需要放在普通的 Kotlin 文件中编译运行。

    Arrow

    Arrow(按其官网写作 Λrrow)是 Kotlin 标准库的函数式“伴侣”。目前主要以下四套件:

    • Arrow Core 提供了核心的数据类型与类型类。
    • Arrow FX 是函数式副作用库,提供了 do-表示法/Monad 推导风格的 DSL。
    • Arrow Optics 用于在 Kotlin 中处理不可变数据模型。
    • Arrow Meta 是 Kotlin 编译器与 IDE 的函数式“伴侣”。

    此外还有若干套件/特性还在孵化中。 关于 Arrow 整体与模式的介绍也在 Arrow Core 的文档中,其中 Functional Programming Glossary 提供了一些使用 Arrow 进行函数式编程的背景知识可供参考。

    一点意外

    在尝试写这些示例时意外发现了一个会导致当前版本的 Kotlin JVM 编译器抛异常的 bug,最小重现代码如下:

    interface WithIntId<T> {
        val T.intId get() = 1
    }
    
    object BooleanWithIntId : WithIntId<Boolean>
    
    val x = BooleanWithIntId.run {
        true.intId
    }

    只影响 Kotlin JVM 编译器,Kotlin JS 与 Kotlin Native 都不存在这个问题。 查了下 YouTrack,看起来是个已知 bug。 不过文中的其他示例代码都能正常编译运行,尽可放心。

    展开全文
  • A:The trait is needed in my current(or previous) position and I know I can handle it well. (这种特点就是我目前(先前)工作所需要的,我知道我能应付自如。) Q:What is your strongest trait(s)?(你...
  • 很有气派 has a stately air to it ​尊称 a term of respect​ 我行我素的做派 a devil-may-care attitude ...个性特征 personality trait 有历史根源 have one's roots in history 勉强自己 inconvenience one...
  • including Swift 3, designing adaptive user interfaces using trait variations, iMessage apps, CloudKit sharing, speech recognition, and SiriKit integration.The aim of this book, therefore, is to teach...
  • 《快学Scala》英文第二版: Scala for the Impatient Second Edition Cay S. Horstmann 目录: 1 THE BASICS A1 1 1.1 The Scala Interpreter 1 1.2 Declaring Values and Variables 4 1.3 Commonly Used Types 5 ...
  • Relative risk --- A comparison of the risk for a disease or trait in individuals who share a particular factor (such as genotype, an environmental exposure, or a drug) versus the risk among individual...
  • Series.es3

    2020-11-26 15:49:03
    e-prime,name&trait程序英文版,series type(4个name,8个trait
  • 英文原文:http://docs.scala-lang.org/style/译文如下:一、类Class / Object / Trait构造函数应该全部声明为一行,除非该行变为“太长”(大约100个字符)。在这种情况下,将每个构造函数参数放在自己的行上,缩进...
  • Scala(5)

    2016-08-10 15:48:51
    Trait英文的解释是:A distinguishing characteristic or quality。这个词的意思就很好解释了Trait在Scala中的作用:用来描述Object的一些特性,但是这特性又不在继承关系下的,即使用继承来表示这种关系很牵强,...
  • scala基础知识--Ordering

    千次阅读 2017-10-18 17:46:00
    trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with ...英文解释 Ordering is a trait whose instances each represent a strategy for sorting instances of a type.Ordering’s compani
  • Trait英文意思是特质和性状(本文称其为特征),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来...
  • 来自:南大NLP01—研究动机自动作文评分(英文叫Automated Essay Scoring,简称AES)旨在使用计算机来根据论文的整体质量或与某些属性(trait)相关的质量来评分...
  • C++ traits编程方法

    2015-09-26 16:19:43
    侯捷老师在《STL 源码剖析》说:traits编程方法...英文意思是attribute,feature等等,中文意思可以解释为特点, 特性。那么type trait就是类型的特性。那什么是类型?类型的特性又有哪些呢?类型也即是用户自定义的类型
  • type traits 之"本质论

    2015-04-22 10:09:08
    侯捷老师在《STL 源码剖析》说:traits编程方法是一...英文意思是attribute,feature等等,中文意思可以解释为特点, 特性。那么type trait就是类型的特性。那什么是类型?类型的特性又有哪些呢?类型也即是用户自定义的
  • traits编程方法

    2012-11-18 16:20:12
    侯捷老师在《STL 源码剖析》说:traits编程方法...英文意思是attribute,feature等等,中文意思可以解释为特点, 特性。那么type trait就是类型的特性。那什么是类型?类型的特性又有哪些呢?类型也即是用户自定义的类型
  • 偏函数的英文是Partial Function(部分函数),从这名字来看很容易理解为函数没有定义完,有哪些情况是函数没有定义完? 函数没有定义完太常见了,因为我们有可能是不知道全部的业务逻辑的,所以偏函数是非常常见的 ...
  • Scala(3)-Traits

    2017-10-02 17:25:42
    Defining a trait Using traits SubtypingTraits中文意思是特征、特质、性状等,用于反映某种事物特有的东西。在专业领域中,有些名词我建议直接使用英文而不翻译,因为一个单词往往有多个中文含义,不同的人翻译...
  • mysql在建库建表时,通常都是使用UTF-8编码(utf8_general_ci),即所有的字段不论是中文还是英文,都是UTF8编码,此时对中文字段进行按拼音首字母进行排序,使用以下方式即可: order by CONVERT( trait_...
  • 英文版Scala程序设计。 在第1版的基础之上,第2版介绍了Scala的最新语言特性,新添了模式匹配、推导式以及高级函数式编程等知识。通过本书,读者还能学会如何使用Scala命令行工具、第三方工具、库以及适用于编辑器...
  • 问题一:What is your strongest trait? 问题二:How would your friends or colleagues describe you? 问题三:What personality traits do you admire? 问题四:What leadership qualities did you develop as ...

空空如也

空空如也

1 2
收藏数 28
精华内容 11
关键字:

trait英文