Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlin extension properties for the Okio.<...>() helper methods #352

Closed
heinrichreimer opened this issue Feb 24, 2018 · 15 comments
Closed

Comments

@heinrichreimer
Copy link

Would be nice for readability if we could use Kotlin extension properties for not having to nest the Okio.source(...), Okio.sink(...), Okio.buffer(...) methods.
That way one could intuitively navigate to a File's BufferedSink using file.sink.buffered.

@swankjesse's Okio file copy example on Twitter would then be reduced to this:

from.source.use { source ->
    to.sink.buffered.use { sink ->
        sink.writeAll(source)
    }
}

Extension properties

val Source.buffered: BufferedSource
    get() = Okio.buffer(this)
val Sink.buffered: BufferedSink 
    get() = Okio.buffer(this)
val OutputStream.sink: Sink
    get() = Okio.sink(this)
val Socket.sink: Sink 
    get() = Okio.sink(this)
val File.sink: Sink
    get() = Okio.sink(this)
val Path.sink: Sink
    get() = Okio.sink(this)
val File.appendingSink: Sink 
    get() = Okio.appendingSink(this)
val InputStream.source: Source 
    get() = Okio.source(this)
val Socket.source: Source 
    get() = Okio.source(this)
val File.source: Source
    get() = Okio.source(this)
val Path.source: Source
    get() = Okio.source(this)
val BufferedSink.outputStream: OutputStream 
    get() = outputStream()
val BufferedSource.inputStream: InputStream
    get() = inputStream()
@swankjesse
Copy link
Member

Please don't do this! Each call to buffered() creates a new Buffer, and that's both inefficient and incorrect.

@heinrichreimer
Copy link
Author

What about extension functions instead of Sink.buffered/Source.buffered extension properties:

fun Source.buffer(): BufferedSource = Okio.buffer(this)
fun Sink.buffer(): BufferedSink = Okio.buffer(this)

That way you the code can be readable while being clear about buffer() returning a new Buffer each time it's called.

Don't get me wrong, I know that one should not write something like this:

val sink = ...
sink.buffered.write(...)
sink.buffered.write(...)
sink.buffered.write(...)

... but rather this

val sink = ...
val bufferedSink = sink.buffered
bufferedSink.write(...)
bufferedSink.write(...)
bufferedSink.write(...)

@heinrichreimer
Copy link
Author

Also something like this in Kotlin:

@file:JvmName("Okio")

package okio

import ...

fun Sink.buffer(): BufferedSink = RealBufferedSink(this)

val File.sink: Sink
    @JvmName("sink") get() {
        return FileOutputStream(this).sink
    }

val OutputStream.sink: Sink
    @Throws(FileNotFoundException::class)
    @JvmName("sink") 
    get() {
        ...
    }

... would actually compile to this in Java:

package okio;

import ...;

public final class Okio {
   @NotNull
   public static final BufferedSink buffer(@NotNull Sink $receiver) {
      return new RealBufferedSink($receiver);
   }

   @NotNull
   public static final Sink sink(@NotNull File $receiver) {
      return sink(new FileOutputStream($receiver));
   }

   @NotNull
   public static final Sink sink(@NotNull OutputStream $receiver) throws FileNotFoundException {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      ...
   }
}

So when changing Okio's static utility methods to extension functions or extension properties, the Java code would still be the same, thus not breaking compatibility to older Okio versions.
Only downside I see here is that there's currently no efficient way to rename the receiver parameter name in the compiled JVM code.

@heinrichreimer
Copy link
Author

It wouldn't be hard to transform the whole Okio class into a Kotlin file with extension functions/properties that would compile to the same JVM signature as the current implementation but at the same time being more readable when writing Kotlin code.
If you'd like, I could submit a pull request.

@JakeWharton
Copy link
Member

JakeWharton commented Feb 24, 2018 via email

@heinrichreimer
Copy link
Author

@JakeWharton That's reasonable.

What about a section in the Readme about Kotlin compatibility?
It just seems to me that writing something like Okio.buffer(Okio.sink(to)) is not a good code style in Kotlin when you could call to.sink().buffer() or something similar instead...

@JakeWharton
Copy link
Member

JakeWharton commented Feb 24, 2018 via email

@heinrichreimer
Copy link
Author

@JakeWharton Agreed!

I'll continue collecting Kotlin-specific "syntactic sugar" here until we have a reasonable value to create a separate artifact.

So for now the list would be:

  • Extension functions/properties for a more functional way to access Okio.source(...), Okio.sink(...), Okio.buffer(...) methods
  • Operator overloading for a more convenient way to concatenate ByteStrings as in ByteString concatenation methods #341
  • DSL for creating and modifying Buffers

@brettwooldridge
Copy link

@JakeWharton

We don't want the dependency on the Kotlin stdlib.

If you don't use any stdlib functions, Kotlin compiles to pure byte-code with no runtime dependency.

Alternatively, if you wish to use Kotlin classes you can invoke kotlinc in a way (-includeRuntime) that builds a jar which includes your classes and kotlin runtime classes, so there is no external dependency. The cost is ~200Kb.

Not that I care one way or another, just sayin' ...

@JakeWharton
Copy link
Member

JakeWharton commented Feb 25, 2018 via email

@brettwooldridge
Copy link

brettwooldridge commented Feb 25, 2018

@JakeWharton ... except when you compile with -Xno-param-assertions and -Xno-call-assertions. Kotlin can produce 100% Kotlin-dependency free bytecode, I guarantee it.

@JakeWharton
Copy link
Member

JakeWharton commented Feb 25, 2018 via email

@brettwooldridge
Copy link

@JakeWharton Ok. Do you recall what dependencies remained?

@JakeWharton
Copy link
Member

JakeWharton commented Feb 25, 2018 via email

@swankjesse
Copy link
Member

Obsoleted by our Okio 2 effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants